react-image-gallery, react-image-lightbox, react-images, and react-photo-gallery are specialized React components designed to handle image presentation, ranging from responsive masonry grids to full-screen lightbox overlays. react-image-gallery offers a comprehensive, all-in-one solution combining thumbnails, slides, and fullscreen modes with extensive configuration. react-image-lightbox is a legacy, lightweight modal specifically for viewing images, now largely unmaintained. react-images provides a modern, highly customizable, headless-friendly architecture for building custom gallery experiences with smooth transitions. react-photo-gallery focuses exclusively on creating responsive, CSS-driven masonry or justified grid layouts without built-in lightbox functionality, requiring composition with other tools for interactive viewing.
When building image-heavy interfaces in React, developers often face a choice between using a monolithic library that does everything or composing smaller, specialized tools. The four packages in questionβreact-image-gallery, react-image-lightbox, react-images, and react-photo-galleryβrepresent different points on this spectrum. Some are aging workhorses, some are deprecated, and others offer modern, composable architectures. Let's break down how they handle layout, interaction, and customization.
The most fundamental difference lies in how these libraries arrange images. Some force a linear slider, while others calculate complex grid geometries.
react-image-gallery uses a linear slider model. It renders one main image with optional thumbnails below or to the side. It handles the math for sliding transitions internally.
// react-image-gallery: Linear slider with thumbnails
import ImageGallery from 'react-image-gallery';
const images = [
{ original: 'img1.jpg', thumbnail: 'thumb1.jpg' },
{ original: 'img2.jpg', thumbnail: 'thumb2.jpg' }
];
function Gallery() {
return <ImageGallery items={images} showThumbnails={true} />;
}
react-image-lightbox has no layout engine. It expects you to trigger it manually, usually from a single image or a simple list you build yourself. It only handles the modal overlay.
// react-image-lightbox: No layout, just a modal trigger
import Lightbox from 'react-image-lightbox';
function SimpleViewer() {
const [index, setIndex] = useState(0);
const images = ['img1.jpg', 'img2.jpg'];
return (
<div>
<img src={images[index]} onClick={() => setIndex((index + 1) % images.length)} />
{index !== null && (
<Lightbox
mainSrc={images[index]}
nextSrc={images[(index + 1) % images.length]}
prevSrc={images[(index + images.length - 1) % images.length]}
onCloseRequest={() => setIndex(null)}
/>
)}
</div>
);
}
react-images provides a flexible container that can act as a grid or a list, but it shines when paired with its own modal. It allows you to define the layout structure more freely than the strict slider of react-image-gallery.
// react-images: Flexible grid/list container
import { Gallery, GalleryImage } from 'react-images';
function CustomGrid() {
const photos = [{ src: 'img1.jpg', caption: 'One' }, { src: 'img2.jpg', caption: 'Two' }];
return (
<Gallery direction='column' maxColumnWidth={400}>
{photos.map((photo) => (
<GalleryImage key={photo.src} source={photo.src} caption={photo.caption} />
))}
</Gallery>
);
}
react-photo-gallery is strictly a layout engine. It calculates the optimal dimensions for a masonry or justified grid based on container width and image aspect ratios. It does not slide or show modals.
// react-photo-gallery: Pure masonry/justified layout
import PhotoGallery from 'react-photo-gallery';
const photos = [
{ src: 'img1.jpg', width: 4, height: 3 },
{ src: 'img2.jpg', width: 3, height: 4 }
];
function MasonryLayout() {
return <PhotoGallery layout='justified' photos={photos} />;
}
How do users interact with the images? Do they click to zoom, swipe to navigate, or scroll?
react-image-gallery bundles interaction. Swiping, keyboard navigation, autoplay, and fullscreen toggling are built-in props. You trade control for convenience.
// react-image-gallery: Built-in interactions
<ImageGallery
items={images}
autoPlay={true}
slideInterval={3000}
showFullscreenButton={true}
useBrowserFullscreen={true}
/>
react-image-lightbox offers basic swipe and keyboard support but feels dated. It lacks touch inertia and smooth transition animations found in modern libraries. It is strictly a "click to open" modal.
// react-image-lightbox: Basic modal interaction
<Lightbox
mainSrc={currentImage}
onCloseRequest={() => closeLightbox()}
onMovePrevRequest={() => setCurrentIndex((prev) => prev - 1)}
onMoveNextRequest={() => setCurrentIndex((prev) => prev + 1)}
// No built-in autoplay or advanced gesture config
/>
react-images separates the viewer logic from the UI. It uses a Modal component that you control, allowing you to inject custom navigation buttons, captions, or loading states easily.
// react-images: Controlled modal interaction
import { Modal } from 'react-images';
function CustomViewer({ current, onClose, onNext, onPrev }) {
return (
<Modal
isOpen={true}
onClose={onClose}
currentImage={current}
images={allImages}
onNextImage={onNext}
onPrevImage={onPrev}
// You can pass custom components for controls here
spinner={() => <MyCustomSpinner />}
/>
);
}
react-photo-gallery delegates interaction entirely. It provides an onClick prop for each image, expecting you to wire it up to a lightbox library (often react-images or a custom solution).
// react-photo-gallery: Delegated interaction
function InteractiveGrid() {
const [open, setOpen] = useState(false);
const handleClick = (event, { photo, index }) => {
setOpen(true);
// Logic to open external lightbox
};
return (
<PhotoGallery
photos={photos}
onClick={handleClick}
renderImage={({ key, photo, imageStyle }) => (
<img key={key} src={photo.src} style={imageStyle} alt={photo.alt} />
)}
/>
);
}
Can you make it look like your design system, or are you stuck with the library's defaults?
react-image-gallery relies on CSS classes and SCSS variables. While you can override styles, the DOM structure is fixed. Deep visual changes often require !important flags or fighting the default CSS.
// react-image-gallery: Class-based overrides
<ImageGallery
items={images}
classPrefix='my-custom-gallery'
renderLeftNav={(onClick, disabled) => <CustomLeftNav onClick={onClick} disabled={disabled} />}
/>
/* CSS: .my-custom-gallery .image-gallery-slide { ... } */
react-image-lightbox is very hard to style. It renders portals with fixed inline styles and limited class hooks. Changing the background opacity or button positions often requires fragile CSS overrides.
// react-image-lightbox: Limited styling hooks
<Lightbox
mainSrc={img}
// Only a few props for styling, mostly relies on internal CSS
imagePadding={10}
reactModalStyle={{ overlay: { zIndex: 1500 } }}
/>
react-images is built for customization. It uses a theme object and allows you to replace almost every internal component (thumbnails, buttons, container) with your own React components.
// react-images: Component replacement
const customTheme = {
container: { backgroundColor: 'rgba(0,0,0,0.9)' },
arrow: { fillColor: '#fff' }
};
<Modal
theme={customTheme}
components={{
Header: ({ onClose }) => <MyHeader close={onClose} />,
Footer: ({ caption }) => <MyCaption text={caption} />
}}
// ...
/>
react-photo-gallery gives you full control over the rendered image tags via renderImage. It applies layout styles (width/height/margin) inline but leaves the rest to you.
// react-photo-gallery: Render prop customization
<PhotoGallery
photos={photos}
renderImage={({ key, photo, imageStyle, containerStyle }) => (
<div key={key} style={containerStyle}>
<img
src={photo.src}
style={{ ...imageStyle, borderRadius: '8px', objectFit: 'cover' }}
/>
</div>
)}
/>
This is the most critical factor for architectural decisions today.
react-image-lightbox is deprecated. The repository is archived, it has not been updated in years, and it does not support React 18 features like Concurrent Mode. It relies on older lifecycle methods that may cause warnings or errors in strict mode. Do not use this for new projects.
// react-image-lightbox: DEPRECATED
// Warning: This package is unmaintained and incompatible with modern React.
// Migration path: Switch to react-images or a headless solution.
react-image-gallery is actively maintained and works with modern React, though its API reflects older patterns (class components internally). It is stable but evolves slowly.
// react-image-gallery: Stable and maintained
// Works with React 18, but API is mostly prop-driven without hooks.
react-images is modern, actively developed, and embraces hooks and functional patterns. It is designed to work seamlessly with the current React ecosystem.
// react-images: Modern architecture
// Uses hooks internally and supports React 18 concurrent features.
const { open, openWithImages } = useGallery();
react-photo-gallery is also well-maintained and focuses purely on the layout math, making it less susceptible to breaking changes in React's rendering engine. It is a safe bet for grid layouts.
// react-photo-gallery: Focused and stable
// Pure layout logic, minimal React internals to break.
You need a main product image, thumbnails below, and a zoom/fullscreen feature. You don't want to build this from scratch.
react-image-gallery<ImageGallery items={productImages} showThumbnails={true} showFullscreenButton={true} />
You need a masonry grid that looks perfect, and when clicked, opens a custom modal with your brand's typography and transitions.
react-photo-gallery + react-imagesreact-photo-gallery for the perfect grid math, and react-images for the customizable modal.// Composition pattern
<PhotoGallery photos={portfolio} onClick={(e, { photo }) => openLightbox(photo)} />
<Modal isOpen={isOpen} images={selectedImage} components={{...}} />
You have an existing app using React 16, and you just need a quick way to click an avatar and see it larger.
react-image-lightbox (Only if migration is impossible)// Legacy pattern
{showLightbox && <Lightbox mainSrc={avatar} onCloseRequest={() => setShow(false)} />}
| Feature | react-image-gallery | react-image-lightbox | react-images | react-photo-gallery |
|---|---|---|---|---|
| Primary Role | All-in-one Slider | Simple Modal | Customizable Viewer | Layout Grid |
| Layout Type | Linear / Carousel | None (Overlay) | Flexible / Grid | Masonry / Justified |
| Customization | Medium (CSS overrides) | Low (Hardcoded) | High (Component injection) | High (Render props) |
| Maintenance | β Active | β Deprecated | β Active | β Active |
| React 18 Ready | β Yes | β No | β Yes | β Yes |
| Best For | Product Carousels | Legacy Quick Fixes | Branded Experiences | Photo Grids |
If you are starting a new project today, never choose react-image-lightbox. Its deprecation makes it a liability for security and compatibility.
For standard use cases like product sliders or documentation galleries where you need features immediately, react-image-gallery remains the most pragmatic choice. It saves weeks of development time.
However, if your application requires a unique look and feel, or if you need a high-performance masonry layout, the composition approach is superior. Pair react-photo-gallery for the grid with react-images for the lightbox. This separation of concerns gives you the best layout math and the most flexible viewer, ensuring your app remains modern and maintainable for years to come.
Choose react-image-gallery if you need a robust, batteries-included solution that handles thumbnails, sliding navigation, and fullscreen modes out of the box. It is ideal for e-commerce product pages or portfolios where you need a standard, feature-rich gallery with minimal custom code and broad browser support.
Avoid react-image-lightbox for new projects as it is deprecated and no longer maintained; it lacks support for modern React patterns like hooks and concurrent rendering. Only consider this if you are maintaining a legacy codebase that already depends on it and cannot afford the refactoring cost to migrate to a modern alternative.
Choose react-images if you require deep customization, animation control, or a 'headless' approach where you want to build a unique user interface rather than using a pre-styled component. It is best suited for design-heavy applications where the gallery interaction needs to feel native to your specific brand and design system.
Choose react-photo-gallery if your primary need is a responsive, aesthetic grid layout (masonry or justified) and you plan to handle the lightbox interaction separately. It is the optimal choice for photography portfolios or news sites where layout density and image cropping logic are more critical than built-in slideshow controls.
A responsive, customizable image gallery component for React
βΆοΈ VIEW LIVE DEMO

| Feature | Description |
|---|---|
| π± Mobile Swipe | Native touch gestures for smooth mobile navigation |
| πΌοΈ Thumbnails | Customizable thumbnail navigation with multiple positions |
| πΊ Fullscreen | Browser fullscreen or CSS-based fullscreen modes |
| π¨ Theming | CSS custom properties for easy styling |
| β¨οΈ Keyboard Nav | Arrow keys, escape, and custom key bindings |
| π RTL Support | Right-to-left language support |
| βοΈ Vertical Mode | Slide vertically instead of horizontally |
| π¬ Custom Slides | Render videos, iframes, or any custom content |
npm install react-image-gallery
import { useRef } from "react";
import ImageGallery from "react-image-gallery";
import "react-image-gallery/styles/image-gallery.css";
import type { GalleryItem, ImageGalleryRef } from "react-image-gallery";
const images: GalleryItem[] = [
{
original: "https://picsum.photos/id/1018/1000/600/",
thumbnail: "https://picsum.photos/id/1018/250/150/",
},
{
original: "https://picsum.photos/id/1015/1000/600/",
thumbnail: "https://picsum.photos/id/1015/250/150/",
},
{
original: "https://picsum.photos/id/1019/1000/600/",
thumbnail: "https://picsum.photos/id/1019/250/150/",
},
];
function MyGallery() {
const galleryRef = useRef<ImageGalleryRef>(null);
return (
<ImageGallery
ref={galleryRef}
items={images}
onSlide={(index) => console.log("Slid to", index)}
/>
);
}
For more examples, see example/App.jsx
items: (required) Array of objects. Available properties:
original - image source URLthumbnail - thumbnail source URLfullscreen - fullscreen image URL (defaults to original)originalHeight - image height (html5 attribute)originalWidth - image width (html5 attribute)loading - "lazy" or "eager" (HTML5 attribute)thumbnailHeight - image height (html5 attribute)thumbnailWidth - image width (html5 attribute)thumbnailLoading - "lazy" or "eager" (HTML5 attribute)originalClass - custom image classthumbnailClass - custom thumbnail classrenderItem - Function for custom rendering a specific slide (see renderItem below)renderThumbInner - Function for custom thumbnail renderer (see renderThumbInner below)originalAlt - image altthumbnailAlt - thumbnail image altoriginalTitle - image titlethumbnailTitle - thumbnail image titlethumbnailLabel - label for thumbnaildescription - description for imagesrcSet - image srcset (html5 attribute)sizes - image sizes (html5 attribute)bulletClass - extra class for the bullet of the iteminfinite: Boolean, default true - loop infinitelylazyLoad: Boolean, default falseshowNav: Boolean, default trueshowThumbnails: Boolean, default truethumbnailPosition: String, default bottom - options: top, right, bottom, leftshowFullscreenButton: Boolean, default trueuseBrowserFullscreen: Boolean, default true - if false, uses CSS-based fullscreenuseTranslate3D: Boolean, default true - if false, uses translate instead of translate3dshowPlayButton: Boolean, default trueisRTL: Boolean, default false - right-to-left modeshowBullets: Boolean, default falsemaxBullets: Number, default undefined - max bullets shown (minimum 3, active bullet stays centered)showIndex: Boolean, default falseautoPlay: Boolean, default falsedisableThumbnailScroll: Boolean, default false - disable thumbnail auto-scrolldisableKeyDown: Boolean, default false - disable keyboard navigationdisableSwipe: Boolean, default falsedisableThumbnailSwipe: Boolean, default falseonErrorImageURL: String, default undefined - fallback image URL for failed loadsindexSeparator: String, default ' / ', ignored if showIndex is falseslideDuration: Number, default 550 - slide transition duration (ms)swipingTransitionDuration: Number, default 0 - transition duration while swiping (ms)slideInterval: Number, default 3000slideOnThumbnailOver: Boolean, default falseslideVertically: Boolean, default false - slide vertically instead of horizontallyflickThreshold: Number, default 0.4 - swipe velocity threshold (lower = more sensitive)swipeThreshold: Number, default 30 - percentage of slide width needed to trigger navigationstopPropagation: Boolean, default false - call stopPropagation on swipe eventsstartIndex: Number, default 0onImageError: Function, callback(event) - overrides onErrorImageURLonThumbnailError: Function, callback(event) - overrides onErrorImageURLonThumbnailClick: Function, callback(event, index)onBulletClick: Function, callback(event, index)onImageLoad: Function, callback(event)onSlide: Function, callback(currentIndex)onBeforeSlide: Function, callback(nextIndex)onScreenChange: Function, callback(isFullscreen)onPause: Function, callback(currentIndex)onPlay: Function, callback(currentIndex)onClick: Function, callback(event)onTouchMove: Function, callback(event) on gallery slideonTouchEnd: Function, callback(event) on gallery slideonTouchStart: Function, callback(event) on gallery slideonMouseOver: Function, callback(event) on gallery slideonMouseLeave: Function, callback(event) on gallery slideadditionalClass: String, additional class for the root noderenderCustomControls: Function, render custom controls on the current sliderenderItem: Function, custom slide renderingrenderThumbInner: Function, custom thumbnail renderingrenderLeftNav: Function, custom left nav componentrenderRightNav: Function, custom right nav componentrenderTopNav: Function, custom top nav component (vertical mode)renderBottomNav: Function, custom bottom nav component (vertical mode)renderPlayPauseButton: Function, custom play/pause buttonrenderFullscreenButton: Function, custom fullscreen buttonuseWindowKeyDown: Boolean, default true - use window or element for key eventsThe following functions can be accessed using refs
play(): starts the slideshowpause(): pauses the slideshowtogglePlay(): toggles between play and pausefullScreen(): enters fullscreen modeexitFullScreen(): exits fullscreen modetoggleFullScreen(): toggles fullscreen modeslideToIndex(index): slides to a specific indexgetCurrentIndex(): returns the current indexPull requests should be focused on a single issue. If you're unsure whether a change is useful or involves a major modification, please open an issue first.
Requires Node.js >= 18.18
git clone https://github.com/xiaolin/react-image-gallery.git
cd react-image-gallery
npm install
npm start
Then open localhost:8001 in a browser.
MIT Β© Xiao Lin