Next.js
If your project is based on Next.js, you may want to take advantage of the next/image component and next/dynamic import to minimize your bundle size.
next/dynamic
In most cases, the lightbox JS code and CSS stylesheets do not need to load with
the initial page load, and loading can be postponed until a user starts
interacting with your application. This fact creates an opportunity to reduce
your initial load bundle size, which can help improve the speed and
responsiveness of your application. To implement this approach in a Next.js
project, you can extract the lightbox-related code and CSS stylesheets into a
separate component and load it dynamically with the next/dynamic import.
The example below demonstrates the use of next/dynamic import.
Sandbox
Edit on StackBlitznext/image
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 via a custom render.slide function. The example below makes use
of placeholder="blur" instead of showing a spinner.
import Image from "next/image";
import {
isImageFitCover,
isImageSlide,
useLightboxProps,
useLightboxState,
} from "yet-another-react-lightbox";
function isNextJsImage(slide) {
return (
isImageSlide(slide) &&
typeof slide.width === "number" &&
typeof slide.height === "number"
);
}
export default function NextJsImage({ slide, offset, rect }) {
const {
on: { click },
carousel: { imageFit },
} = useLightboxProps();
const { currentIndex } = useLightboxState();
const cover = isImageSlide(slide) && isImageFitCover(slide, imageFit);
if (!isNextJsImage(slide)) return undefined;
const width = !cover
? Math.round(
Math.min(rect.width, (rect.height / slide.height) * slide.width),
)
: rect.width;
const height = !cover
? Math.round(
Math.min(rect.height, (rect.width / slide.width) * slide.height),
)
: rect.height;
return (
<div style={{ position: "relative", width, height }}>
<Image
fill
alt=""
src={slide}
loading="eager"
draggable={false}
placeholder={slide.blurDataURL ? "blur" : undefined}
style={{
objectFit: cover ? "cover" : "contain",
cursor: click ? "pointer" : undefined,
}}
sizes={`${Math.ceil((width / window.innerWidth) * 100)}vw`}
onClick={
offset === 0 ? () => click?.({ index: currentIndex }) : undefined
}
/>
</div>
);
}
import Lightbox from "yet-another-react-lightbox";
import "yet-another-react-lightbox/styles.css";
import NextJsImage from "@/components/NextJsImage";
import image1 from "@/public/images/image01.jpg";
import image2 from "@/public/images/image02.jpg";
import image3 from "@/public/images/image03.jpg";
// ...
return (
<Lightbox
open={open}
close={() => setOpen(false)}
slides={[image1, image2, image3]}
render={{ slide: NextJsImage }}
/>
);
Live Demo
Sandbox
Edit on StackBlitzWith Thumbnails Plugin
In the same way you used next/image to optimize your slide images, you can use
it to optimize your thumbnails. You can replace the standard <img> element
with next/image via a custom render.thumbnails function.
import Lightbox from "yet-another-react-lightbox";
import Thumbnails from "yet-another-react-lightbox/plugins/thumbnails";
import "yet-another-react-lightbox/styles.css";
import "yet-another-react-lightbox/plugins/thumbnails.css";
import NextJsImage from "@/components/NextJsImage";
import image1 from "@/public/images/image01.jpg";
import image2 from "@/public/images/image02.jpg";
import image3 from "@/public/images/image03.jpg";
// ...
return (
<Lightbox
open={open}
close={() => setOpen(false)}
slides={[image1, image2, image3]}
render={{ slide: NextJsImage, thumbnail: NextJsImage }}
plugins={[Thumbnails]}
/>
);
With Zoom Plugin
The Zoom plugin doesn't work well with the Next.js image component. You can use the following approach to take advantage of the Next.js image optimization when using Zoom plugin.
import Lightbox from "yet-another-react-lightbox";
import Zoom from "yet-another-react-lightbox/plugins/zoom";
import "yet-another-react-lightbox/styles.css";
import image1 from "@/public/images/image01.jpg";
import image2 from "@/public/images/image02.jpg";
import image3 from "@/public/images/image03.jpg";
// ...
const images = [
image1,
image2,
image3,
// ...
];
const imageSizes = [16, 32, 48, 64, 96, 128, 256, 384];
const deviceSizes = [640, 750, 828, 1080, 1200, 1920, 2048, 3840];
function nextImageUrl(src, size) {
return `/_next/image?url=${encodeURIComponent(src)}&w=${size}&q=75`;
}
const slides = images.map(({ src, width, height }) => ({
width,
height,
src: nextImageUrl(src, width),
srcSet: imageSizes
.concat(...deviceSizes)
.filter((size) => size <= width)
.map((size) => ({
src: nextImageUrl(src, size),
width: size,
height: Math.round((height / width) * size),
})),
}));
// ...
return (
<Lightbox
open={open}
close={() => setOpen(false)}
slides={slides}
plugins={[Zoom]}
/>
);
Live Demo
Sandbox
Edit on StackBlitzWith Rotate Plugin
The next/image render function above doesn't apply image rotation, so it needs
a few changes to work with the Rotate plugin. The function
below reads the slide rotation with the useSlideRotation hook, fits the image
into the transposed slide rect (with width and height swapped) for quarter-turn
rotations, and rotates the wrapper element. The flexShrink: 0 style prevents
the slide container from shrinking the unrotated image box when it is wider than
the slide.
import Image from "next/image";
import {
isImageFitCover,
isImageSlide,
useLightboxProps,
useLightboxState,
useSlideRotation,
} from "yet-another-react-lightbox";
function isNextJsImage(slide) {
return (
isImageSlide(slide) &&
typeof slide.width === "number" &&
typeof slide.height === "number"
);
}
export default function NextJsImage({ slide, offset, rect }) {
const {
on: { click },
carousel: { imageFit },
} = useLightboxProps();
const { currentIndex } = useLightboxState();
const rotation = useSlideRotation(slide);
const cover = isImageSlide(slide) && isImageFitCover(slide, imageFit);
if (!isNextJsImage(slide)) return undefined;
// quarter-turn rotations fit the image into the transposed rect
const fitRect =
rotation % 180 !== 0 ? { width: rect.height, height: rect.width } : rect;
const width = !cover
? Math.round(
Math.min(fitRect.width, (fitRect.height / slide.height) * slide.width),
)
: fitRect.width;
const height = !cover
? Math.round(
Math.min(fitRect.height, (fitRect.width / slide.width) * slide.height),
)
: fitRect.height;
return (
<div
style={{
position: "relative",
width,
height,
rotate: rotation ? `${rotation}deg` : undefined,
flexShrink: 0,
}}
>
<Image
fill
alt=""
src={slide}
loading="eager"
draggable={false}
placeholder={slide.blurDataURL ? "blur" : undefined}
style={{
objectFit: cover ? "cover" : "contain",
cursor: click ? "pointer" : undefined,
}}
sizes={`${Math.ceil((width / window.innerWidth) * 100)}vw`}
onClick={
offset === 0 ? () => click?.({ index: currentIndex }) : undefined
}
/>
</div>
);
}
The same function can be used as a custom render.thumbnail function to rotate
thumbnails as well.
import Lightbox from "yet-another-react-lightbox";
import Rotate from "yet-another-react-lightbox/plugins/rotate";
import Thumbnails from "yet-another-react-lightbox/plugins/thumbnails";
import "yet-another-react-lightbox/styles.css";
import "yet-another-react-lightbox/plugins/thumbnails.css";
import NextJsImage from "@/components/NextJsImage";
import image1 from "@/public/images/image01.jpg";
import image2 from "@/public/images/image02.jpg";
import image3 from "@/public/images/image03.jpg";
// ...
return (
<Lightbox
open={open}
close={() => setOpen(false)}
slides={[image1, image2, image3]}
render={{ slide: NextJsImage, thumbnail: NextJsImage }}
plugins={[Rotate, Thumbnails]}
/>
);
Alternatively, the approach described in With Zoom Plugin uses the built-in image slide, which supports rotation out of the box.