Lightweight React lightbox component. This is a trimmed-down version of the yet-another-react-lightbox that provides essential lightbox features and slick UX with just 4.6KB bundle size.
- Built for React: works with React 18+
- UX: supports keyboard, mouse, touchpad and touchscreen navigation
- Zoom: zoom is supported out of the box
- Performance: preloads a fixed number of images without compromising performance or UX
- Responsive: responsive images with automatic resolution switching are supported out of the box
- Customization: customize any UI element or add your own custom slides
- No bloat: supports only essential lightbox features
- TypeScript: type definitions come built-in in the package
https://github.com/igordanchenko/yet-another-react-lightbox-lite
- Live demo - https://stackblitz.com/edit/yet-another-react-lightbox-lite
- Examples - https://stackblitz.com/edit/yet-another-react-lightbox-lite-examples
https://github.com/igordanchenko/yet-another-react-lightbox-lite/releases
- React 18+
- Node 18+
- modern ESM-compatible bundler
npm install yet-another-react-lightbox-lite
import { useState } from "react";
import Lightbox from "yet-another-react-lightbox-lite";
import "yet-another-react-lightbox-lite/styles.css";
export default function App() {
const [index, setIndex] = useState<number>();
return (
<>
<button type="button" onClick={() => setIndex(0)}>
Open Lightbox
</button>
<Lightbox
slides={[
{ src: "/image1.jpg" },
{ src: "/image2.jpg" },
{ src: "/image3.jpg" },
]}
index={index}
setIndex={setIndex}
/>
</>
);
}
To utilize responsive images with automatic resolution switching, provide
srcset
images in the slide srcSet
array.
<Lightbox
slides={[
{
src: "/image1x3840.jpg",
srcSet: [
{ src: "/image1x320.jpg", width: 320, height: 213 },
{ src: "/image1x640.jpg", width: 640, height: 427 },
{ src: "/image1x1200.jpg", width: 1200, height: 800 },
{ src: "/image1x2048.jpg", width: 2048, height: 1365 },
{ src: "/image1x3840.jpg", width: 3840, height: 2560 },
],
},
// ...
]}
// ...
/>
If your project is based on Next.js, you may want to take
advantage of the
next/image
component. The next/image
component provides a more efficient way to handle
images in your Next.js project. You can replace the standard <img>
element
with next/image
with the following render.slide
render function.
<Lightbox
render={{
slide: ({ slide, rect }) => {
const width =
slide.width && slide.height
? Math.round(
Math.min(rect.width, (rect.height / slide.height) * slide.width),
)
: rect.width;
const height =
slide.width && slide.height
? Math.round(
Math.min(rect.height, (rect.width / slide.width) * slide.height),
)
: rect.height;
return (
<Image
src={slide.src}
alt={slide.alt || ""}
width={width}
height={height}
loading="eager"
draggable={false}
blurDataURL={(slide as any).blurDataURL}
style={{
minWidth: 0,
minHeight: 0,
maxWidth: "100%",
maxHeight: "100%",
objectFit: "contain",
}}
/>
);
},
}}
// ...
/>
Yet Another React Lightbox Lite comes with CSS stylesheet that needs to be imported in your app.
import "yet-another-react-lightbox-lite/styles.css";
The lightbox component accepts the following props.
Type: Slide[]
An array of slides to display in the lightbox. This prop is required. By default, the lightbox supports only image slides. You can add support for custom slides through a custom render function (see example below).
Image slide props:
src
- image source (required)alt
- imagealt
attribute
Type: number | undefined
Current slide index. This prop is required.
Type: (index: number | undefined) => void
A callback to update current slide index state. This prop is required.
Type: { [key: string]: string }
Custom UI labels / translations.
<Lightbox
labels={{
Previous: t("Previous"),
Next: t("Next"),
Close: t("Close"),
}}
// ...
/>
Type: object
Toolbar settings.
buttons
- custom toolbar buttons (type:ReactNode[]
)fixed
- iftrue
, the toolbar is positioned statically above the carousel
Usage example:
<Lightbox
toolbar={{
fixed: true,
buttons: [
<button
type="button"
className="yarll__button"
onClick={() => {
// ...
}}
>
<ButtonIcon />
</button>,
],
}}
// ...
/>
Type: object
Carousel settings.
preload
- the lightbox preloads(2 * preload + 1)
slides (default:2
)imageProps
- custom image slide attributes
Usage example:
<Lightbox
carousel={{
preload: 5,
imageProps: { crossOrigin: "anonymous" },
}}
// ...
/>
Type: object
Controller settings.
closeOnPullUp
- iftrue
, close the lightbox on pull-up gesture (default:true
)closeOnPullDown
- iftrue
, close the lightbox on pull-down gesture (default:true
)closeOnBackdropClick
- iftrue
, close the lightbox when the backdrop is clicked (default:true
)
Usage example:
<Lightbox
controller={{
closeOnPullUp: false,
closeOnPullDown: false,
closeOnBackdropClick: false,
}}
// ...
/>
Type: object
An object providing custom render functions.
<Lightbox
render={{
slide: ({ slide, rect, zoom, current }) => (
<CustomSlide {...{ slide, rect, zoom, current }} />
),
slideHeader: ({ slide, rect, zoom, current }) => (
<SlideHeader {...{ slide, rect, zoom, current }} />
),
slideFooter: ({ slide, rect, zoom, current }) => (
<SlideFooter {...{ slide, rect, zoom, current }} />
),
controls: () => <CustomControls />,
iconPrev: () => <IconPrev />,
iconNext: () => <IconNext />,
iconClose: () => <IconClose />,
}}
// ...
/>
Render custom slide type, or override the default image slide implementation.
Parameters:
slide
- slide object (type:Slide
)rect
- slide rect size (type:Rect
)zoom
- current zoom level (type:number
)current
- iftrue
, the slide is the current slide in the viewport (type:boolean
)
Render custom elements above each slide.
Render custom elements below or over each slide. By default, the content is
rendered right under the slide. Alternatively, you can use
position: "absolute"
to position the extra elements relative to the slide.
For example, you can use the slideFooter
render function to add slides
descriptions.
<Lightbox
render={{
slideFooter: ({ slide }) => (
<div style={{ marginBlockStart: 16 }}>{slide.description}</div>
),
}}
// ...
/>
Render custom controls or additional elements in the lightbox (use absolute positioning).
For example, you can use the render.controls
render function to implement
slides counter.
<Lightbox
render={{
controls: () =>
index !== undefined && (
<div style={{ position: "absolute", top: 16, left: 16 }}>
{index + 1} of {slides.length}
</div>
),
}}
// ...
/>
Render custom Previous
icon.
Render custom Next
icon.
Render custom Close
icon.
Type: { [key in Slot]?: SlotCSSProperties }
Customization slots styles allow you to specify custom CSS styles or override
--yarll__*
CSS variables by passing your custom styles through to the
corresponding lightbox elements.
Supported customization slots:
portal
- lightbox portal (root)carousel
- lightbox carouselslide
- lightbox slideimage
- lightbox slide imagetoolbar
- lightbox toolbarbutton
- lightbox buttonicon
- lightbox icon
Usage example:
<Lightbox
styles={{
portal: { "--yarll__backdrop_color": "rgba(0, 0, 0, 0.6)" },
}}
// ...
/>
Type: string
CSS class of the lightbox root element. You can use this class name to provide module-scoped style overrides.
Type: object
Zoom settings.
disabled
- disable zoom on image slidessupports
- zoom-enabled custom slide types
Usage example:
<Lightbox
zoom={{ supports: ["custom-slide-type"] }}
// ...
/>
You can add custom slide attributes with the following module augmentation.
declare module "yet-another-react-lightbox-lite" {
interface GenericSlide {
description?: string;
}
}
You can add custom slide types through module augmentation and render them with
the render.slide
render function.
Here is an example demonstrating video slides implementation.
declare module "yet-another-react-lightbox-lite" {
interface SlideVideo extends GenericSlide {
type: "video";
src: string;
poster: string;
width: number;
height: number;
}
interface SlideTypes {
video: SlideVideo;
}
}
// ...
<Lightbox
slides={[
{
type: "video",
src: "/media/video.mp4",
poster: "/media/poster.jpg",
width: 1280,
height: 720,
},
]}
render={{
slide: ({ slide }) =>
slide.type === "video" ? (
<video
controls
playsInline
poster={slide.poster}
width={slide.width}
height={slide.height}
style={{ maxWidth: "100%", maxHeight: "100%" }}
>
<source type="video/mp4" src={slide.src} />
</video>
) : undefined,
}}
// ...
/>;
// Lightbox.tsx
import LightboxComponent, {
LightboxProps,
} from "yet-another-react-lightbox-lite";
import "yet-another-react-lightbox-lite/styles.css";
export default function Lightbox(props: LightboxProps) {
return <LightboxComponent {...props} />;
}
// App.tsx
import { lazy, Suspense, useState } from "react";
import slides from "./slides";
const Lightbox = lazy(() => import("./Lightbox"));
export default function App() {
const [index, setIndex] = useState<number>();
return (
<>
<button type="button" onClick={() => setIndex(0)}>
Open Lightbox
</button>
{index !== undefined && (
<Suspense>
<Lightbox slides={slides} index={index} setIndex={setIndex} />
</Suspense>
)}
</>
);
}
By default, the lightbox hides the browser window scrollbar and prevents
document <body>
from scrolling underneath the lightbox by assigning the
height: 100%; overflow: hidden;
styles to the document <body>
element.
If this behavior causes undesired side effects in your case, and you prefer not
to use this feature, you can turn it off by assigning the
yarll__no_scroll_lock
class to the lightbox.
<Lightbox
className="yarll__no_scroll_lock"
// ...
/>
However, if you keep the body scroll lock feature on, you may notice a visual
layout shift of some fixed-positioned page elements when the lightbox opens. To
address this, you can assign the yarll__fixed
CSS class to your
fixed-positioned elements to keep them in place. Please note that the
fixed-positioned element container should not have its own border or padding
styles. If that's the case, you can always add an extra wrapper that just
defines the fixed position without visual styles.
The library exports the following experimental hooks that you may find helpful
in customizing lightbox functionality. All experimental hooks are currently
exported with the unstable_
prefix.
You can use the useZoom
hook to build your custom zoom controls.
import { unstable_useZoom as useZoom } from "yet-another-react-lightbox-lite";
The hook provides an object with the following props:
rect
- slide rectzoom
- current zoom level (numeric value between1
and8
)maxZoom
- maximum zoom level (1
if zoom is not supported on the current slide)offsetX
- horizontal slide position offsetoffsetY
- vertical slide position offsetchangeZoom
- change zoom levelchangeOffsets
- change position offsets
MIT © 2024 Igor Danchenko