preskok/ui

Async Image

Compound async image with lazy loading, skeleton placeholder, and error fallback.

Installation

pnpm dlx shadcn@latest add @preskok/async-image

Usage

Basic usage with default loading skeleton and error fallback:

import { AsyncImage } from "@/components/ui/preskok-ui/async-image"
 
export function AsyncImageExample() {
  return (
    <AsyncImage.Root
      src="https://example.com/image.jpg"
      alt="Description"
      width={300}
      height={200}
    />
  )
}

With custom slots:

<AsyncImage.Root src="/photo.jpg" alt="Photo" width={400} height={300}>
  <AsyncImage.Loading className="bg-muted flex items-center justify-center">
    <span>Loading…</span>
  </AsyncImage.Loading>
  <AsyncImage.Error>Failed to load image</AsyncImage.Error>
</AsyncImage.Root>

Disable lazy loading (load immediately):

<AsyncImage.Root
  src="/hero.jpg"
  alt="Hero"
  width={800}
  height={400}
  lazyLoad={false}
/>

Props

AsyncImage.Root

PropTypeDefaultDescription
srcstring—Image URL. Empty string triggers error state.
altstring""Accessible alt text for the image.
widthnumber | string—Width of the container and image.
heightnumber | string—Height of the container and image.
imgPropsOmit<React.ImgHTMLAttributes<HTMLImageElement>, "src" | "alt" | "width" | "height">—Props passed to the underlying <img> when loaded.
loadingDelayMsnumber0Optional delay in ms before transitioning from loading to loaded (e.g. to avoid flicker).
lazyLoadfalse | IntersectionObserverInit{ threshold: 0.01, rootMargin: "75%" }Lazy load when in view. Set to false to load immediately.
onLoadingEnd() => void—Called when the image has loaded successfully.
onErrorFallback() => void—Called when the image fails to load.
childrenReact.ReactNode—Optional compound slots: AsyncImage.Loading, AsyncImage.Error, AsyncImage.Content.

All other div props (e.g. className, style) are forwarded to the root container.

Slots

  • AsyncImage.Loading — Rendered while status === "loading". Default: skeleton placeholder.
  • AsyncImage.Error — Rendered when status === "error". Default: icon + "Image unavailable".
  • AsyncImage.Content — Rendered when status === "loaded". Default: the <img> element. Override to customize the loaded image element.