Lightbox
A full-screen image and video gallery lightbox component using @reelkit/react-lightbox.
Features
Installation
Don't forget to import the styles:
Icons
lucide-react for icons. If you prefer a different icon library, use renderControls and renderNavigation to provide your own.Quick Start
The LightboxOverlay component displays images in fullscreen. Pass an array of LightboxItem objects and control visibility with a nullable index.
Live Demo
Click a thumbnail to open the lightbox. Use arrow keys or swipe to navigate.
Video Slides (Opt-in)
Video support is fully opt-in and tree-shakeable — image-only usage pays zero extra bundle cost. Import useVideoSlideRenderer and wire its return values into LightboxOverlay. The hook handles loading states, sound management, and video lifecycle automatically.
How it works
- The hook returns
SoundProvider— wrap your overlay in it for mute/unmute to work - Videos autoplay (muted by default) when the slide becomes active
- A shared video element is reused across slides for iOS sound continuity
- Sound button appears automatically on video slides with a reactive mute toggle
- Items without
type: 'video'render as images (backward compatible)
Customization
Custom Controls
Use renderControls to replace the default close button, counter, and fullscreen toggle. Compose with the exported sub-components:
Custom Info Overlay
Use renderInfo to replace the default title/description gradient, or pass renderInfo={() => null} to hide it entirely:
Custom Navigation
Use renderNavigation to replace the default prev/next arrows:
Custom Slide
Use renderSlide for fully custom slide content. Return null to fall back to the default image slide:
Content Loading & Error Handling
The lightbox tracks per-slide loading and error states. A spinner shows while content loads; a broken-image icon shows for failed media. Errored URLs are cached so revisiting shows the error instantly without retrying.
Lifecycle Callbacks
When using renderSlide, call these callbacks to control the loading indicator:
| Callback | When to call |
|---|---|
| onReady | Image loaded or video started playing. Clears loading and error states. |
| onWaiting | Video is buffering mid-playback. Shows the loading indicator. |
| onError | Content failed to load. Shows error overlay and caches the URL as broken. |
Custom Loading & Error UI
Replace the default spinner and error icon with custom components:
API Reference
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| isOpen | boolean | required | Controls lightbox visibility |
| images | LightboxItem[] | required | Array of images to display |
| ariaLabel | string | 'Image gallery' | Accessible label for the dialog region; announced by screen readers when the lightbox opens |
| initialIndex | number | 0 | Starting image index |
| transitionFn | TransitionTransformFn | slideTransition | Slide transition function. Import a built-in (slideTransition, flipTransition, lightboxFadeTransition, lightboxZoomTransition) or pass a custom one. Defaults to slideTransition when omitted. |
| apiRef | MutableRefObject<ReelApi> | - | Ref to access Reel API |
| renderControls | (props: ControlsRenderProps) => ReactNode | - | Custom controls, replaces default close button, counter, and fullscreen toggle |
| renderNavigation | (props: NavigationRenderProps) => ReactNode | - | Custom navigation, replaces default prev/next arrows |
| renderInfo | (props: InfoRenderProps) => ReactNode | - | Custom info overlay, replaces default title + description gradient. Return null to hide. |
| renderSlide | (props: SlideRenderProps) => ReactNode | null | - | Custom slide rendering. Receives { item, index, size, isActive, onReady, onWaiting, onError }. Return null to fall back to default. |
| renderLoading | (props: { item: LightboxItem; activeIndex: number }) => ReactNode | - | Custom loading indicator, replaces default spinner |
| renderError | (props: { item: LightboxItem; activeIndex: number }) => ReactNode | - | Custom error indicator, replaces default error icon |
Callbacks
| Prop | Type | Description |
|---|---|---|
| onClose | () => void | Called when lightbox closes |
| onSlideChange | (index: number) => void | Called after slide change |
Reel Props (proxied)
These props are forwarded to the underlying Reel component.
| Prop | Type | Default | Description |
|---|---|---|---|
| loop | boolean | false | Enable infinite loop |
| enableNavKeys | boolean | true | Enable keyboard navigation |
| enableWheel | boolean | true | Enable mouse wheel navigation |
| wheelDebounceMs | number | 200 | Wheel debounce duration (ms) |
| transitionDuration | number | 300 | Transition animation duration (ms) |
| swipeDistanceFactor | number | 0.12 | Swipe threshold (0-1) |
| swipeToCloseDirection | 'up' | 'down' | 'up' | Direction of the swipe-to-close gesture on mobile |
Types
LightboxItem
ControlsRenderProps
NavigationRenderProps
SlideRenderProps
InfoRenderProps
Sub-Components
Reusable sub-components for composing custom controls via renderControls.
CloseButton
Default X close button.
Counter
Image counter pill showing "1 / 3".
FullscreenButton
Fullscreen toggle button (Maximize/Minimize icon).
SoundButton
Mute/unmute toggle button for video slides (Volume2/VolumeX icon). Included automatically in renderControls from useVideoSlideRenderer. For standalone use inside custom controls, access sound state via useSoundState.
Hooks
useVideoSlideRenderer
Hook for opt-in video support. Returns renderSlide, renderControls, and SoundProvider — wrap the overlay in SoundProvider and pass the render functions.
useFullscreen
Moved
useFullscreen was removed from @reelkit/react-lightbox. Import it from @reelkit/react instead.Hook for managing fullscreen state with cross-browser support.
Transitions
Pass any TransitionTransformFn via the transitionFn prop. Importing only the transition you use lets the bundler tree-shake the rest. Defaults to slideTransition when omitted.
| Function | From | Description |
|---|---|---|
| slideTransition | @reelkit/react-lightbox | Standard horizontal slide (default) |
| lightboxFadeTransition | @reelkit/react-lightbox | Crossfade between images |
| flipTransition | @reelkit/react-lightbox | 3D card flip effect |
| lightboxZoomTransition | @reelkit/react-lightbox | Zoom in from smaller to normal size |
Custom Transition Function
Author your own TransitionTransformFn and pass it via transitionFn. The signature mirrors core slider transitions.
CSS Classes
All UI elements use plain CSS classes (not CSS modules) that can be targeted with higher-specificity selectors in a stylesheet loaded after @reelkit/react-lightbox/styles.css. For color, size, and z-index changes, prefer the CSS custom properties documented in the Theming section below.
| Class | Component | Description |
|---|---|---|
| .rk-lightbox-overlay | Overlay | Root container (full-screen backdrop) |
| .rk-lightbox-spinner | Overlay | Default loading spinner |
| .rk-lightbox-img-error | Overlay | Error state container (broken image/video) |
| .rk-lightbox-img-error-text | Overlay | Error state text label |
| .rk-lightbox-swipe-hint | Overlay | Mobile swipe hint |
| .rk-lightbox-controls-left | Controls | Top-left controls container |
| .rk-lightbox-btn | Controls | Control buttons (fullscreen, etc.) |
| .rk-lightbox-close | Controls | Close button |
| .rk-lightbox-counter | Controls | Image counter chip |
| .rk-lightbox-nav | Navigation | Navigation arrows (both) |
| .rk-lightbox-nav-prev | Navigation | Previous arrow |
| .rk-lightbox-nav-next | Navigation | Next arrow |
| .rk-lightbox-info | Info | Title/description container |
| .rk-lightbox-title | Info | Image title |
| .rk-lightbox-description | Info | Image description |
| .rk-lightbox-slide | Slide | Slide container |
| .rk-lightbox-img | Slide | Image element |
| .rk-lightbox-video-container | VideoSlide | Video slide container (opt-in) |
| .rk-lightbox-video-element | VideoSlide | Video element (opt-in) |
| .rk-lightbox-video-poster | VideoSlide | Video poster image (opt-in) |
Theming
Every color, size, z-index, and transition lives in a CSS custom property. Override one or many at :root (or any ancestor of the lightbox) to retheme without touching component source.
| Token | Default | Controls |
|---|---|---|
| --rk-lightbox-overlay-bg | #000 | Full-screen backdrop color |
| --rk-lightbox-overlay-z | 9999 | Overlay z-index |
| --rk-lightbox-top-shade-height | 80px | Top gradient scrim height |
| --rk-lightbox-top-shade-bg | linear-gradient(rgba(0,0,0,0.6), transparent) | Top gradient scrim color |
| --rk-lightbox-edge-padding | 16px | Edge inset for close / nav / top-left controls |
| --rk-lightbox-controls-gap | 12px | Gap between top-left controls |
| --rk-lightbox-transition | 0.2s | Button hover transition duration |
| --rk-lightbox-blur | 8px | Backdrop blur radius for buttons / chips |
| --rk-lightbox-btn-bg | rgba(0, 0, 0, 0.5) | Default background for close, nav, small buttons |
| --rk-lightbox-btn-bg-hover | rgba(255, 255, 255, 0.2) | Hover background for close, nav, small buttons |
| --rk-lightbox-btn-fg | #fff | Icon color for close, nav, small buttons |
| --rk-lightbox-btn-size | 36px | Small button size (fullscreen toggle, etc.) |
| --rk-lightbox-close-size | 40px | Close button size |
| --rk-lightbox-nav-size | 48px | Prev/next arrow size |
| --rk-lightbox-nav-opacity | 0.7 | Idle opacity of prev/next arrows |
| --rk-lightbox-counter-fg | #fff | Counter text color |
| --rk-lightbox-counter-bg | rgba(0, 0, 0, 0.5) | Counter chip background |
| --rk-lightbox-counter-size | 14px | Counter font size |
| --rk-lightbox-counter-padding | 6px 12px | Counter chip padding |
| --rk-lightbox-counter-radius | 20px | Counter chip border-radius |
| --rk-lightbox-spinner-size | 28px | Default spinner width/height |
| --rk-lightbox-spinner-track | rgba(255, 255, 255, 0.2) | Spinner track color |
| --rk-lightbox-spinner-fg | #fff | Spinner indicator color |
| --rk-lightbox-spinner-duration | 0.8s | Spinner rotation duration |
| --rk-lightbox-error-fg | rgba(255, 255, 255, 0.4) | Error icon + text color |
| --rk-lightbox-error-text-size | 13px | Error message font size |
| --rk-lightbox-info-bg | linear-gradient(transparent, rgba(0,0,0,0.8)) | Caption scrim gradient |
| --rk-lightbox-info-padding | 24px | Caption inner padding |
| --rk-lightbox-title-size | 18px | Title font size |
| --rk-lightbox-description-size | 14px | Description font size |
| --rk-lightbox-info-fg | #fff | Caption text color |
| --rk-lightbox-hint-fg | rgba(255, 255, 255, 0.5) | Swipe hint text color |
| --rk-lightbox-hint-bg | rgba(0, 0, 0, 0.3) | Swipe hint chip background |
| --rk-lightbox-hint-duration | 3s | Swipe hint fade-in/out total duration |
| --rk-lightbox-video-bg | #000 | Letterbox background behind <video> |
Drop the snippet below into a stylesheet loaded after @reelkit/react-lightbox/styles.css.
Accessibility
The overlay root is a modal dialog (role="dialog", aria-modal="true"). Set ariaLabel to change the screen-reader announcement; it defaults to "Image gallery". Each slide carries role="group", aria-roledescription="slide", and aria-label="Image N of M".
The lightbox captures focus on open and returns it to the trigger on close. Tab and Shift+Tab cycle through focusable elements inside; focus that escapes (click outside, programmatic focus) gets pulled back. Implemented with captureFocusForReturn and createFocusTrap from @reelkit/core.
Keyboard Shortcuts
| Key | Action |
|---|---|
| ArrowLeft | Previous image |
| ArrowRight | Next image |
| Escape | Close lightbox (or exit fullscreen if active) |