yet-another-react-lightbox-lite
v1.5.0
Published
Lightweight React lightbox component
Downloads
635
Maintainers
Readme
Yet Another React Lightbox Lite
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.
Overview
- 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
Documentation
https://github.com/igordanchenko/yet-another-react-lightbox-lite
Examples
- Live demo - https://stackblitz.com/edit/yet-another-react-lightbox-lite
- Examples - https://stackblitz.com/edit/yet-another-react-lightbox-lite-examples
Changelog
https://github.com/igordanchenko/yet-another-react-lightbox-lite/releases
Requirements
- React 18+
- Node 18+
- modern ESM-compatible bundler
Installation
npm install yet-another-react-lightbox-lite
Minimal Setup Example
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}
/>
</>
);
}
Responsive Images
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 },
],
},
// ...
]}
// ...
/>
Next.js Image
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",
}}
/>
);
},
}}
// ...
/>
API
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.
slides
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
index
Type: number | undefined
Current slide index. This prop is required.
setIndex
Type: (index: number | undefined) => void
A callback to update current slide index state. This prop is required.
labels
Type: { [key: string]: string }
Custom UI labels / translations.
<Lightbox
labels={{
Previous: t("Previous"),
Next: t("Next"),
Close: t("Close"),
}}
// ...
/>
toolbar
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>,
],
}}
// ...
/>
carousel
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" },
}}
// ...
/>
controller
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,
}}
// ...
/>
render
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 />,
}}
// ...
/>
slide: ({ slide, rect, zoom, current }) => ReactNode
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
)
slideHeader: ({ slide, rect, zoom, current }) => ReactNode
Render custom elements above each slide.
slideFooter: ({ slide, rect, zoom, current }) => ReactNode
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>
),
}}
// ...
/>
controls: () => ReactNode
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>
),
}}
// ...
/>
iconPrev: () => ReactNode
Render custom Previous
icon.
iconNext: () => ReactNode
Render custom Next
icon.
iconClose: () => ReactNode
Render custom Close
icon.
styles
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)" },
}}
// ...
/>
className
Type: string
CSS class of the lightbox root element. You can use this class name to provide module-scoped style overrides.
Custom Slide Attributes
You can add custom slide attributes with the following module augmentation.
declare module "yet-another-react-lightbox-lite" {
interface GenericSlide {
description?: string;
}
}
Custom Slides
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,
}}
// ...
/>;
Code Splitting (Suspense)
// 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>
)}
</>
);
}
Body Scroll Lock
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.
Hooks (experimental)
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.
useZoom
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
License
MIT © 2024 Igor Danchenko