react-image-gallery, react-images, and react-photo-gallery are three distinct approaches to displaying collections of images in React applications. react-image-gallery is a comprehensive, opinionated carousel solution offering thumbnails, fullscreen modes, and swipe gestures out of the box. react-images is a lightweight, headless-friendly lightbox component focused purely on the modal viewing experience without built-in grid layouts. react-photo-gallery specializes in creating responsive, masonry-style grids that automatically calculate layout but relies on external libraries for the actual image viewing (lightbox) functionality.
Building image galleries in React often forces a trade-off between layout control and feature completeness. The three leading packagesβreact-image-gallery, react-images, and react-photo-galleryβsolve different parts of this problem. Understanding their architectural boundaries is critical to avoiding "wrapper hell" or fighting against a library's core design.
The fundamental difference lies in what each library considers its primary responsibility.
react-image-gallery is an opinionated carousel. It manages the entire lifecycle of the view: the main image, the thumbnail strip, navigation buttons, and the fullscreen overlay. It assumes you want a linear sequence of images.
// react-image-gallery: All-in-one carousel
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-images is a lightbox-only component. It has no concept of a grid or thumbnails. It simply takes a list of images and an index, then renders a modal overlay. You must build the trigger mechanism yourself.
// react-images: Pure lightbox overlay
import Lightbox from 'react-images';
function Gallery() {
const [isOpen, setIsOpen] = useState(false);
const photos = [{ src: 'img1.jpg', alt: 'Image 1' }];
return (
<>
<button onClick={() => setIsOpen(true)}>View Image</button>
<Lightbox
isOpen={isOpen}
onClose={() => setIsOpen(false)}
images={photos}
/>
</>
);
}
react-photo-gallery is a layout engine. It calculates the optimal positioning of images in a responsive grid (masonry or row-based) but renders only <img> tags. It provides no interaction logic for viewing the full-size image.
// react-photo-gallery: Layout calculation only
import Gallery from 'react-photo-gallery';
const photos = [{ src: 'img1.jpg', width: 4, height: 3 }];
function Grid() {
return <Gallery layout="masonry" photos={photos} />;
}
How the libraries handle image dimensions dictates the visual feel of your application.
react-image-gallery forces a uniform container. It crops or fits images into a fixed aspect ratio defined by the slider container. This ensures stability but can crop important parts of an image if not configured carefully.
// react-image-gallery: Fixed container constraints
<ImageGallery
items={images}
slideInterval={3000}
// Images are scaled to fit the slide container
slideOnThumbnailOver={true}
/>
react-images delegates layout entirely to you. Since it only handles the modal, the trigger elements (your grid) can be styled however you like. The lightbox itself scales the image to fit the viewport while maintaining aspect ratio.
// react-images: You control the trigger layout
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(200px, 1fr))' }}>
{photos.map((p, i) => (
<img
key={i}
src={p.src}
onClick={() => setCurrentImage(i)}
style={{ width: '100%', height: 'auto' }}
/>
))}
</div>
react-photo-gallery excels at fluid layouts. It accepts width/height ratios for each image and calculates the exact CSS needed to create a tight masonry or justified row layout without gaps. It handles the math so you don't have to.
// react-photo-gallery: Automatic masonry calculation
const photos = [
{ src: 'tall.jpg', width: 3, height: 4 },
{ src: 'wide.jpg', width: 4, height: 2 }
];
<Gallery
layout="masonry"
photos={photos}
renderImage={({ key, photo, margin }) => (
<img
key={key}
src={photo.src}
style={{ margin, width: photo.width }}
/>
)}
/>
The developer experience diverges sharply when implementing user interactions like clicking to zoom or swiping.
react-image-gallery includes swipe gestures, keyboard navigation, and thumbnail clicking internally. You enable these via boolean props. Customizing the behavior often requires overriding internal methods or CSS.
// react-image-gallery: Built-in interactions
<ImageGallery
items={images}
showPlayButton={true}
showFullscreenButton={true}
onSlide={(current, previous) => console.log('Slide changed')}
/>
react-images requires you to wire up the state. It provides the modal mechanics (swipe to close, arrow keys to navigate), but you must manage the currentIndex and isOpen state in your parent component.
// react-images: Manual state wiring
const [current, setCurrent] = useState(0);
<Lightbox
images={photos}
currentImage={current}
onClose={() => setIsOpen(false)}
onClickPrev={() => setCurrent(prev => prev - 1)}
onClickNext={() => setCurrent(prev => prev + 1)}
onClickThumbnail={index => setCurrent(index)}
/>
react-photo-gallery provides an onClick prop for individual images but does nothing else. To get a lightbox, you must compose it with another library. This is the most flexible but also the most verbose approach.
// react-photo-gallery: Composition required
import Lightbox from 'react-images';
function CompositeGallery() {
const [open, setOpen] = useState(false);
const [current, setCurrent] = useState(0);
const handleClick = (event, { photo, index }) => {
setCurrent(index);
setOpen(true);
};
return (
<>
<Gallery photos={photos} onClick={handleClick} />
{open && <Lightbox images={photos} currentImage={current} onClose={() => setOpen(false)} />}
</>
);
}
A critical architectural decision factor is the long-term viability of these packages.
react-photo-gallery has historically faced maintenance challenges. While still functional for basic grids, it lacks modern React patterns (like full hook support in older versions) and has seen periods of inactivity. For new projects requiring complex masonry layouts, developers often evaluate newer alternatives like react-masonry-css or native CSS Grid solutions to avoid dependency risks.
react-images and react-image-gallery are actively maintained and follow modern React standards. react-images in particular has undergone significant refactoring to support headless UI patterns and better accessibility.
Despite their different goals, these libraries share common ground in how they handle media assets.
All three libraries expect an array of objects, though the required keys differ slightly. They all rely on the developer to provide source URLs.
// Common pattern across all three
const assets = [
{ src: 'url-to-large.jpg', thumbnail: 'url-to-small.jpg' }, // react-image-gallery
{ src: 'url-to-large.jpg', alt: 'Description' }, // react-images
{ src: 'url-to-large.jpg', width: 4, height: 3 } // react-photo-gallery
];
Modern versions of react-image-gallery and react-images include built-in keyboard navigation (Esc to close, arrows to navigate) and ARIA labels. react-photo-gallery relies on standard HTML image semantics unless customized.
// react-images: Built-in keyboard nav
// Pressing 'Escape' automatically triggers onClose
<Lightbox isOpen={true} onClose={close} images={photos} />
// react-image-gallery: Built-in ARIA
// Slider includes role="region" and aria-label automatically
<ImageGallery items={items} />
Both react-image-gallery and react-images implement touch handlers for swipe gestures on mobile devices. react-photo-gallery is static and does not include touch logic for viewing.
// react-image-gallery: Swipe to change slide
<ImageGallery items={items} swipeThreshold={50} />
// react-images: Swipe to close or navigate
<Lightbox images={photos} isSwipeDisabled={false} />
| Feature | react-image-gallery | react-images | react-photo-gallery |
|---|---|---|---|
| Primary Role | Full Carousel | Lightbox Modal | Grid Layout Engine |
| Layout Style | Linear Slider | None (You build it) | Masonry / Rows |
| Thumbnails | β Built-in | β Manual Implementation | β Manual Implementation |
| Fullscreen | β Built-in | β Built-in (Modal) | β Requires Composition |
| Aspect Ratio | Fixed Container | Viewport Fitted | Calculated (Fluid) |
| Dependencies | None (Self-contained) | None | None (but needs lightbox) |
| Best For | Product Sliders, Simple Portfolios | Custom Grids + Modal | Pinterest-style Layouts |
Choosing the right package depends on whether you need a product or a primitive.
react-image-gallery is a product. It is a finished UI component. Use it when you need a standard carousel quickly and don't need to deviate from the traditional "main image + thumbnails" pattern. It saves time but limits design flexibility.
react-images is a primitive. It solves the hardest part of image viewing (the modal logic, accessibility, and gestures) while leaving the layout to you. Use it when you have a custom design system and need a lightbox that blends seamlessly into your existing grid.
react-photo-gallery is a math helper. It solves the complex problem of arranging images of varying aspect ratios into a clean grid. Use it only for the layout, and plan to pair it with react-images or a similar library to handle the actual image interaction. Be sure to verify its current maintenance status before committing to it in a long-term enterprise project.
Choose react-image-gallery if you need a complete, all-in-one carousel solution with built-in thumbnails, navigation arrows, and fullscreen support. It is ideal for product showcases, portfolios, or any scenario where users expect a traditional 'slider' interface with minimal configuration. Avoid this if you need a custom masonry grid layout, as it strictly enforces a carousel structure.
Choose react-images if you already have a custom grid layout and only need a robust, accessible lightbox overlay for viewing images. It is perfect for developers who want full control over the gallery structure (using CSS Grid or Flexbox) but don't want to build the modal logic, keyboard navigation, and touch gestures from scratch. Do not use this if you need built-in thumbnail strips or automatic grid calculations.
Choose react-photo-gallery if your primary requirement is a responsive, masonry-style grid that handles image aspect ratios and column breaking automatically. It is the best fit for photography portfolios or Pinterest-style layouts where visual density matters more than slide navigation. You must pair this with a separate lightbox library (like react-images) since it does not provide image viewing capabilities itself.
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