Skip to content
WANCRAFT

B.190Cards & Containers

Lightbox Gallery

A thumbnail grid that opens a full-screen <dialog> lightbox: FLIP from the thumbnail, swipe, pinch / double-click zoom and a thumbnail strip.

NewReactCSSTailwindadvanced

Overview

Lightbox Gallery turns a grid of thumbnails into a full-screen viewer built on the native <dialog> element. The clicked image grows out of its exact thumbnail: a FLIP transform plus an animated clip-path undoes the crop, so the open is seamless. On close it shrinks back into the thumbnail of whichever image is showing, and focus returns to that thumbnail. Inside the viewer, arrow keys, the side buttons or a swipe move between images. Neighbours sit in their own slots on a sliding track, so a swipe shows the next image arriving, and the images two steps away are preloaded. Pinch, double-click, double-tap, Ctrl + wheel, the + and - keys, or the zoom button zoom in, and drag, wheel or the arrow keys pan the zoomed image. A caption, a "3 / 12" counter and a thumbnail strip complete the chrome. Escape, a click on the backdrop or a swipe down closes the viewer. With reduced motion everything fades.

01Installation

Method 01 · Registry

CLI verified

Uses the shadcn CLI in a project initialised with shadcn init. Installs the file to components/ui/lightbox-gallery.tsx and adds its dependencies.

npx shadcn@latest add https://wancraft.my/r/lightbox-gallery.json

Method 02 · Copy and paste

  1. 1. No extra dependencies — React and Tailwind CSS only.
  2. 2. Copy the source from the Code tab into components/ui/lightbox-gallery.tsx.
  3. 3. Import it and adjust props.
Dependencies
None
Peer
react ≥ 18, tailwindcss ≥ 4
Added
13 Oct 2026
Updated
—

02Usage

The default configuration. Change properties above to generate a tailored example in the Usage tab.

example.tsx
import { LightboxGallery, type LightboxImage } from "@/components/ui/lightbox-gallery";

export function LightboxGalleryExample() {
  const images: LightboxImage[] = [
    { src: "/photos/harbour.jpg", alt: "Fishing boats in the harbour at dawn", caption: "Harbour, 6:10am", width: 1600, height: 1067 },
    { src: "/photos/terraces.jpg", alt: "Rice terraces after rain", caption: "Terraces after the rain", width: 1067, height: 1600 },
    { src: "/photos/market.jpg", alt: "Lanterns over a night market", caption: "Night market", width: 1600, height: 1600 },
  ];

  return (
    <LightboxGallery images={images} label="Field trip" />
  );
}

03Props

PropTypeDefaultDescription
imagesLightboxImage[]—Required. { src, alt, caption?, width?, height?, thumbSrc? }[]. width / height let the viewer fit images before they load.
columnsnumber4Grid columns from the sm breakpoint (two below it).
gapnumber8Grid gap in px.
aspectRatiostring"1/1"Thumbnail crop; the open animation undoes it with a clip-path.
roundednumber10Thumbnail corner radius in px.
thumbnailsbooleantrueThumbnail strip inside the viewer.
loopbooleantrueWrap from the last image to the first (and back).
maxZoomnumber3Largest zoom factor.
durationnumber380Open / close (FLIP) duration in ms.
backdrop"dark" | "theme""dark"Always-dark backdrop, or one that follows the page theme.
labelstring"Gallery"Accessible name of the grid; the dialog is "{label} viewer".
onIndexChange(index: number, open: boolean) => void—Called when the viewer opens, moves or closes.
accentstring"#FF7448"Focus ring and current-thumbnail colour.
classNamestring—Extra classes for the root.

04Features

  • 01Native <dialog> + showModal(): top layer, inert page, Escape handled by the browser
  • 02FLIP open from the clicked thumbnail, with a clip-path that undoes its crop; it closes back into the current image's thumbnail
  • 03Swipe, arrow keys or buttons between images; neighbours slide in on a track and images two steps away are preloaded
  • 04Zoom with pinch, double-click / double-tap, Ctrl + wheel, + / - keys or the zoom button; pan with drag, wheel or the arrow keys
  • 05Caption, a "3 / 12" counter and a thumbnail strip that keeps the current image centred
  • 06Swipe down or click the backdrop to close; with reduced motion every animation becomes a fade
motion · tweeninput · clickinput · draginput · keyboardinput · press

05Accessibility

  • Thumbnails are buttons with the image's alt text plus "image 3 of 12, open viewer"; they announce aria-haspopup="dialog".
  • The viewer is a labelled modal <dialog>, so the page behind is inert and focus starts on the close button. Escape closes it and focus returns to the thumbnail of the image that was showing.
  • Arrow Left / Right change image, and Home / End jump to the ends. + / - / 0 zoom in, out and reset; while zoomed the arrow keys pan instead.
  • A polite live region announces "Image 3 of 12" with the caption on every change; the strip buttons are labelled and mark the current image with aria-current.
  • Only the current image is exposed to assistive tech; the neighbour slots are aria-hidden with empty alt text.
  • With prefers-reduced-motion, open, close and image changes are short fades.

06Limitations

  • The return animation only plays when the target thumbnail is on screen; otherwise the viewer fades out.
  • Zooming scales the displayed image. For very large originals, pass a smaller src and handle high-resolution swapping yourself.