Lightbox

Full-screen viewer for images and video: swipe or arrow keys to browse, pinch / wheel / double-tap zoom with pan, thumbnails, captions, download and a file-details panel for the media library. Dialog pattern: focus trap, Esc closes, focus returns to the thumbnail.

Radix Dialog + Carousel + usePanZoom media/lightbox.tsx, media/use-pan-zoom.tstheme · material · accent · density · font

Moda office · 4 photos · click to view, ← → to browse, + − to zoom

Installation

$ pnpm add @vamos-ui/react

The whole kit as one package — get updates by bumping the version. Add the styles to globals.css: @import "@vamos-ui/react/styles.css";

Usage

import { Lightbox, LightboxTrigger } from "@vamos-ui/react/media";

<Lightbox items={photos} download>
  {photos.map((p, i) => (
    <LightboxTrigger key={p.id} index={i} aria-label={`Enlarge image: ${p.alt}`}>
      <Image src={p.src} alt="" aspect="1/1" />
    </LightboxTrigger>
  ))}
</Lightbox>

Keys: ←/→ browse (mirrored in RTL), Home/End, + / − / 0 zoom, Esc close.

The counter is aria-live: "Image 4 of 12: Office entrance".

Examples

Zoom

Pinch, wheel, double-tap, or + − 0. While zoomed, dragging pans instead of switching images. backdrop="dim" shows the page behind.

Images and video

Items with kind: "video" play in the kit's Video player. Opened from a Gallery with lightbox={{ backdrop: "blur" }}.

Controlled

open and index live in your state (or your router's ?photo=); no trigger needed.

Closed · opens on “Workshop — Atlas Lojistik, 6 Nov”

File details (media library)

info adds a panel with alt text, file name, type, size, dimensions and uploader, formatted with the current locale.

API

Lightbox

PropTypeDefault
items
Images and videos. MediaSource: { src, alt, width?, height?, caption?, kind?, poster?, placeholder? }.
(MediaAsset | MediaSource)[]—
open / defaultOpen / onOpenChange
Controlled or uncontrolled.
boolean / boolean / (o) => void— / false
index / defaultIndex / onIndexChange
Current item. Sync it to the URL yourself if you want.
number / number / (i) => void— / 0
zoom
Image zoom with pan.
boolean | { max?: number }true (max 3)
thumbnails
Thumbnail strip.
booleantrue with 2+ items
download
Download button for the original.
booleanfalse
info
File-details panel toggle (uses MediaAsset fields).
booleanfalse
loop
Wrap at the ends.
booleantrue
backdrop
solid: the scene at 96%; dim: --overlay (chrome switches to dark); blur: overlay + blur (the only blurred layer).
"solid" | "dim" | "blur""solid"
chrome
minimal hides the caption and thumbnails.
"minimal" | "full""full"
labels
viewer, counter, close, zoomIn… (dictionary namespace lightbox).
Partial<LightboxLabels>—
children
Triggers or a gallery; they can call useLightbox().
ReactNode—

LightboxTrigger

PropTypeDefault
index
Item to open.
number—
asChild
Use your own element (a Button).
booleanfalse

useLightbox()

PropTypeDefault
→
Open the surrounding viewer from custom UI.
{ open(index?), close(), isOpen, index }—

usePanZoom(options)

PropTypeDefault
min / max / step
Zoom range and button step.
number1 / 3 / 0.5
doubleTapScale
Double click / tap toggles to this.
number2
→ returns
Spread bind on the viewport and style on the content.
{ bind, style, scale, zoomed, zoomIn, zoomOut, reset, zoomTo }—