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.
Moda office · 4 photos · click to view, ← → to browse, + − to zoom
Installation
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
| Prop | Type | Default |
|---|---|---|
itemsImages and videos. MediaSource: { src, alt, width?, height?, caption?, kind?, poster?, placeholder? }. | (MediaAsset | MediaSource)[] | — |
open / defaultOpen / onOpenChangeControlled or uncontrolled. | boolean / boolean / (o) => void | — / false |
index / defaultIndex / onIndexChangeCurrent item. Sync it to the URL yourself if you want. | number / number / (i) => void | — / 0 |
zoomImage zoom with pan. | boolean | { max?: number } | true (max 3) |
thumbnailsThumbnail strip. | boolean | true with 2+ items |
downloadDownload button for the original. | boolean | false |
infoFile-details panel toggle (uses MediaAsset fields). | boolean | false |
loopWrap at the ends. | boolean | true |
backdropsolid: the scene at 96%; dim: --overlay (chrome switches to dark); blur: overlay + blur (the only blurred layer). | "solid" | "dim" | "blur" | "solid" |
chromeminimal hides the caption and thumbnails. | "minimal" | "full" | "full" |
labelsviewer, counter, close, zoomIn… (dictionary namespace lightbox). | Partial<LightboxLabels> | — |
childrenTriggers or a gallery; they can call useLightbox(). | ReactNode | — |
LightboxTrigger
| Prop | Type | Default |
|---|---|---|
indexItem to open. | number | — |
asChildUse your own element (a Button). | boolean | false |
useLightbox()
| Prop | Type | Default |
|---|---|---|
→Open the surrounding viewer from custom UI. | { open(index?), close(), isOpen, index } | — |
usePanZoom(options)
| Prop | Type | Default |
|---|---|---|
min / max / stepZoom range and button step. | number | 1 / 3 / 0.5 |
doubleTapScaleDouble click / tap toggles to this. | number | 2 |
→ returnsSpread bind on the viewport and style on the content. | { bind, style, scale, zoomed, zoomIn, zoomOut, reset, zoomTo } | — |