Image Viewer

A dialog image viewer with navigation, zoom, rotation, and panning.

Loading example…

Installation

bunx --bun shadcn@latest add @sui/image-viewer

Install with the shadcn CLI or use the shared @workspace/ui package. Follow the installation guide to configure the registry, load styles, and choose import aliases.

Usage

import { Button } from "@workspace/ui/components/button";
import { ImageViewer } from "@workspace/ui/components/image-viewer";
import { useState } from "react";

const images = ["/photos/first.jpg", "/photos/second.jpg"];

export function Gallery() {
  const [open, setOpen] = useState(false);
  const [initialIndex, setInitialIndex] = useState(0);
  return (
    <>
      {images.map((src, index) => (
        <Button
          key={src}
          type="button"
          variant="ghost"
          className="h-auto overflow-hidden rounded-xl p-0"
          onClick={() => {
            setInitialIndex(index);
            setOpen(true);
          }}
        >
          <img src={src} alt={`Photo ${index + 1}`} />
        </Button>
      ))}
      <ImageViewer
        images={images}
        initialIndex={initialIndex}
        open={open}
        onClose={() => setOpen(false)}
        alt="Photo gallery"
      />
    </>
  );
}

Opening and navigation

Opening is controlled through open and onClose. Supply one image URL or an array. With multiple images, navigation controls and thumbnails let the reader switch images. Zoom, rotation, reset, and panning actions are available inside the viewer. Empty image arrays render nothing. Wheel and pinch gestures zoom, and dragging pans. Arrow keys navigate, +/- zoom, and 0 resets the transform. Loading failures provide a retry action. Replace the image paths in the snippet with your own assets.

Controlled image index

initialIndex sets the initial uncontrolled image, using zero-based indexes. For external selection, pass index and update it in onIndexChange. Selecting another image resets its transform. Reset is enabled only after zooming, rotating, or panning changes the image. The example opens a specific image and displays the selected zero-based index. Opening an uncontrolled viewer restores initialIndex; changing initialIndex while open also updates the selection.

Loading example…

Labels, focus, and portal container

The viewer uses the shared Dialog for modal focus management and dismissal. alt describes the images, and labels customizes dialog, navigation, zoom, rotation, thumbnail, and position announcements. Function labels receive one-based image positions, while index and onIndexChange use zero-based indexes.

Use container to choose a Portal container when embedding the viewer in a local themed region. The glass effect applies to its surrounding dialog surface; the image itself remains clear.

API reference

PropTypeDefault / behavior
imagesstring | string[]Required image URL(s).
openbooleanRequired controlled dialog state.
onClose() => voidRequired close callback.
indexnumberControlled zero-based image index.
initialIndexnumber0; initial uncontrolled image.
onIndexChange(index: number) => voidReports selected zero-based index.
altstringlabels.defaultAlt or "Image".
containerHTMLElement | ShadowRoot | RefObject<HTMLElement | ShadowRoot | null> | nullOptional Portal container.
labelsPartial<ImageViewerLabels>English action and image labels.
glassbooleanfalse.

The module exports ImageViewerProps and ImageViewerLabels. The underlying modal behavior is documented in the Base UI Dialog API.