Full-screen media lightbox with carousel navigation
After installation import package styles at the root of your application:
@mantine/lightbox is a full-screen media lightbox built on embla carousel . Click any image to open the lightbox:
The slides prop accepts an array of LightboxSlideData objects. There are three slide types – image (default), video and custom:
All slides are mounted at once, so images are loaded lazily by default – only the active slide uses loading="eager" , every other slide uses loading="lazy" and is downloaded by the browser as it comes into view. This way opening a gallery with a large number of slides does not start a download for every image at once. Thumbnails are always lazy – note that thumbSrc falls back to src , so set it to a smaller image if the originals are large.
Set the loading property on an image slide to override this per slide, for example to eagerly preload the slide next to the one that is opened first:
Use the following props to control built-in interactions:
Enable image zoom with the withZoom prop. On desktop, click an image to zoom in, scroll wheel to adjust zoom level, and drag or use arrow keys to pan when zoomed. On mobile, double-tap to zoom and pinch to adjust. Use zoomMaxScale to change the maximum zoom scale ( 3 by default):
Click image to zoom, scroll to adjust, drag to pan when zoomed. Press Z to toggle zoom via keyboard.
Enable the bottom thumbnail strip with withThumbnails . Click a thumbnail to navigate to that slide. Toggle visibility at runtime with the T keyboard shortcut or the toolbar button – the strip is animated with the transitionDuration prop value:
By default, thumbnails are rendered as <img> elements: image slides use thumbSrc or src , video slides use thumbSrc or poster , custom slides use thumbSrc . Set renderThumb on any slide to render custom thumbnail content instead – for example, a <video> element when the thumbnail is a small video rather than an image. The function receives { active } payload, active is true for the thumbnail of the current slide. Rendered content is placed inside a 64x64px button, size it with width: 100% and height: 100% :
Combine withZoom , withThumbnails , withFullscreen , and withDownload for the full experience:
Set loop to enable infinite wrapping at the ends of the slide list:
By default, programmatic navigation (arrow buttons, keyboard, controlled index changes) snaps instantly. Set withSlideTransition to animate slide changes. The animation is handled by embla – use emblaOptions={{ duration: 40 }} to change its speed (embla duration is not in milliseconds, values between 20 and 60 are recommended):
The carousel behavior can be customized with the emblaOptions prop – it is passed directly to the underlying embla carousel instance. For example, you can change drag behavior or scroll animation speed.
loop and startIndex are managed by the loop and currentIndex props and cannot be set through emblaOptions . A watchDrag callback is still called, but dragging is always disabled while the image is zoomed, so the zoom gesture is not interrupted:
zIndex controls the z-index of the overlay and the content elements, 400 by default:
By default, the overlay fades in and the content pops in – it is scaled from 95% to 100% while fading. Use transitionProps to change the animation of the content – the overlay always fades. transitionProps accepts the same options as the Transition component ( transition , duration , timingFunction ). Use transitionDuration as a shorthand to change only the duration ( 200 by default):
Set transitionProps={{ duration: 0 }} to open and close the lightbox instantly, without any animation:
On mobile, swiping down closes the lightbox. This is enabled by default. Set closeOnSwipeDown={false} to disable:
Set closeOnClickOutside to close the lightbox when the empty space around the current slide content is clicked – clicks on the image, video or any custom slide content are ignored, as well as clicks on the toolbar, navigation buttons, caption and thumbnails. The option is disabled by default to prevent accidental closing:
Mount Lightbox.Provider once in your app and open the lightbox from anywhere using the static Lightbox methods (aliases of the default lightbox store actions):
By default, Lightbox.Provider and the static Lightbox.open/close/next/prev/setIndex methods use the shared lightboxStore . To run several independent lightboxes, create an isolated store with actions bound to it using createLightbox and pass the store to the store prop:
To subscribe to the state of any lightbox store in a component, use the useLightboxStore hook:
Set type: 'video' on a slide to render a <video> element. Videos are automatically paused when navigating away from the slide. The poster image is used as the thumbnail, set thumbSrc to use a different image or renderThumb to render a custom thumbnail, for example a muted <video> element (see the last slide in the demo below):
Opens on a video slide with autoPlay. Navigate away to see the video pause automatically.
Set type: 'custom' with a render function for fully custom slide content. Use renderThumb or thumbSrc to provide the thumbnail – custom slides do not have a default one:
Override the default toolbar items with the toolbarItems prop. Each item has a key , icon , label , onClick , and optional position ( 'left' or 'right' ).
toolbarItems can be either an array or a function that receives the current lightbox state and handlers – use the function form to build items that depend on the internal state, for example the thumbnails, fullscreen and zoom toggles. The function receives the following payload:
Use the built-in toolbar item factories for common actions:
For full layout control, compose sub-components directly:
Note that Lightbox.Thumbnails renders only when withThumbnails is set on Lightbox.Root .
Available sub-components:
All strings rendered by the lightbox are defined in the labels prop. Pass the labels that you want to change – the rest fall back to the default English values, which are exported as DEFAULT_LABELS :
To change labels for all lightboxes in your application, set labels in default props of the Lightbox component.
Escape always closes the lightbox. Other shortcuts are active when withKeyboardEvents is set (default). They are ignored when focus is inside an input, textarea or media element anywhere in the lightbox, and when focus is on a button, link or other widget inside slide content – so controls in custom slides keep their own keyboard handling. F / T / Z only work when the corresponding feature is enabled: