Lightbox
Fullscreen dialog for viewing image or video media, with optional gallery navigation.
Examples
Section titled “Examples”Default
Section titled “Default”function LightboxStory({ defaultIndex, media, ...args}: LightboxStoryArgs & {media: LightboxProps['media']}): React.JSX.Element { const [isOpen, setIsOpen] = useState(false);
return ( <> <Button label="Open lightbox" onClick={() => setIsOpen(true)} /> <Lightbox {...args} defaultIndex={defaultIndex} isOpen={isOpen} media={media} onOpenChange={setIsOpen} /> </> );}
<LightboxStory {...args} media={imageMedia[0]} />Gallery
Section titled “Gallery”function LightboxStory({ defaultIndex, media, ...args}: LightboxStoryArgs & {media: LightboxProps['media']}): React.JSX.Element { const [isOpen, setIsOpen] = useState(false);
return ( <> <Button label="Open lightbox" onClick={() => setIsOpen(true)} /> <Lightbox {...args} defaultIndex={defaultIndex} isOpen={isOpen} media={media} onOpenChange={setIsOpen} /> </> );}
<LightboxStory {...args} media={imageMedia} />function LightboxStory({ defaultIndex, media, ...args}: LightboxStoryArgs & {media: LightboxProps['media']}): React.JSX.Element { const [isOpen, setIsOpen] = useState(false);
return ( <> <Button label="Open lightbox" onClick={() => setIsOpen(true)} /> <Lightbox {...args} defaultIndex={defaultIndex} isOpen={isOpen} media={media} onOpenChange={setIsOpen} /> </> );}
<LightboxStory {...args} media={videoMedia} />Mixed Media
Section titled “Mixed Media”function LightboxStory({ defaultIndex, media, ...args}: LightboxStoryArgs & {media: LightboxProps['media']}): React.JSX.Element { const [isOpen, setIsOpen] = useState(false);
return ( <> <Button label="Open lightbox" onClick={() => setIsOpen(true)} /> <Lightbox {...args} defaultIndex={defaultIndex} isOpen={isOpen} media={media} onOpenChange={setIsOpen} /> </> );}
<LightboxStory {...args} media={mixedMedia} />With Captions
Section titled “With Captions”function LightboxStory({ defaultIndex, media, ...args}: LightboxStoryArgs & {media: LightboxProps['media']}): React.JSX.Element { const [isOpen, setIsOpen] = useState(false);
return ( <> <Button label="Open lightbox" onClick={() => setIsOpen(true)} /> <Lightbox {...args} defaultIndex={defaultIndex} isOpen={isOpen} media={media} onOpenChange={setIsOpen} /> </> );}
<LightboxStory {...args} media={imageMedia} />Zoom Enabled
Section titled “Zoom Enabled”function LightboxStory({ defaultIndex, media, ...args}: LightboxStoryArgs & {media: LightboxProps['media']}): React.JSX.Element { const [isOpen, setIsOpen] = useState(false);
return ( <> <Button label="Open lightbox" onClick={() => setIsOpen(true)} /> <Lightbox {...args} defaultIndex={defaultIndex} isOpen={isOpen} media={media} onOpenChange={setIsOpen} /> </> );}
<LightboxStory {...args} media={imageMedia[0]} />Autoplay Video
Section titled “Autoplay Video”function LightboxStory({ defaultIndex, media, ...args}: LightboxStoryArgs & {media: LightboxProps['media']}): React.JSX.Element { const [isOpen, setIsOpen] = useState(false);
return ( <> <Button label="Open lightbox" onClick={() => setIsOpen(true)} /> <Lightbox {...args} defaultIndex={defaultIndex} isOpen={isOpen} media={media} onOpenChange={setIsOpen} /> </> );}
<LightboxStory {...args} media={videoMedia} />Controlled Index
Section titled “Controlled Index”function ControlledIndexStory(args: LightboxStoryArgs): React.JSX.Element { const [isOpen, setIsOpen] = useState(false); const [index, setIndex] = useState(1);
return ( <> <div style={{display: 'flex', gap: 8}}> <Button label="Open gallery" onClick={() => setIsOpen(true)} /> <Button label="Show first" onClick={() => setIndex(0)} /> <Button label="Show last" onClick={() => setIndex(imageMedia.length - 1)} /> </div> <Lightbox {...args} index={index} isOpen={isOpen} media={imageMedia} onIndexChange={setIndex} onOpenChange={setIsOpen} /> </> );}
<ControlledIndexStory {...args} />Use Lightbox Hook
Section titled “Use Lightbox Hook”function HookStory(): React.JSX.Element { const lightbox = useLightbox({media: imageMedia[0]});
return ( <> <Button label="Open from hook" onClick={() => lightbox.open()} /> {lightbox.element} </> );}
<HookStory />Use Lightbox Gallery
Section titled “Use Lightbox Gallery”function HookGalleryStory(): React.JSX.Element { const lightbox = useLightbox({media: imageMedia, hasZoom: true});
return ( <> <div style={{ display: 'grid', gap: 12, gridTemplateColumns: 'repeat(3, minmax(0, 140px))', }}> {imageMedia.map((item, index) => ( <div key={item.src} {...lightbox.getTriggerProps(index)} style={{ border: 0, borderRadius: 6, cursor: 'pointer', overflow: 'hidden', }}> <img alt={item.alt} src={item.src} style={{ aspectRatio: '4 / 3', display: 'block', objectFit: 'cover', width: '100%', }} /> </div> ))} </div> {lightbox.element} </> );}
<HookGalleryStory />Lightbox
Section titled “Lightbox”Fullscreen dialog for viewing image or video media, with optional gallery navigation.
| Prop | Type | Default | Description |
|---|---|---|---|
className | string | — | Additional CSS class names applied to the dialog. |
data-testid | string | — | Test ID applied to the dialog. |
defaultIndex | number | 0 | Initial gallery index for uncontrolled usage. |
hasAutoPlay | boolean | false | Whether videos autoplay when opened. |
hasZoom | boolean | false | Whether images can be double-clicked to zoom. |
index | number | — | Controlled gallery index. |
isOpen* | boolean | — | Whether the lightbox is open. |
media* | LightboxMedia | ReadonlyArray<LightboxMedia> | — | Media to display. |
onIndexChange | (index: number) => void | — | Called when gallery index changes. |
onOpenChange* | (isOpen: boolean) => void | — | Called when the lightbox requests an open-state change. |
ref | Ref<HTMLDialogElement> | — | Ref forwarded to the dialog element. |
style | CSSProperties | — | Inline styles applied to the dialog. |