react-image-gallery vs react-images vs react-photo-gallery
Architecting Responsive Image Galleries in React
react-image-galleryreact-imagesreact-photo-gallery

Architecting Responsive Image Galleries in React

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.

Npm Package Weekly Downloads Trend

3 Years

Github Stars Ranking

Stat Detail

Package
Downloads
Stars
Size
Issues
Publish
License
react-image-gallery03,942123 kB146 months agoMIT
react-images02,335-505 years agoMIT
react-photo-gallery02,014-807 years agoMIT

Architecting Responsive Image Galleries: Carousel vs. Lightbox vs. Masonry

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.

🎑 Core Architecture: Slider vs. Modal vs. Grid

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} />;
}

πŸ“ Layout Control: Fixed Aspect Ratios vs. Fluid Masonry

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 }} 
    />
  )} 
/>

πŸ–±οΈ Interaction Patterns: Built-in vs. Composed

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)} />}
    </>
  );
}

⚠️ Maintenance and Deprecation Status

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.

πŸ—οΈ Similarities: Shared Foundations

Despite their different goals, these libraries share common ground in how they handle media assets.

1. πŸ–ΌοΈ Image Object Structure

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
];

2. β™Ώ Accessibility Features

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} />

3. πŸ“± Touch Support

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} />

πŸ“Š Summary: Capability Matrix

Featurereact-image-galleryreact-imagesreact-photo-gallery
Primary RoleFull CarouselLightbox ModalGrid Layout Engine
Layout StyleLinear SliderNone (You build it)Masonry / Rows
Thumbnailsβœ… Built-in❌ Manual Implementation❌ Manual Implementation
Fullscreenβœ… Built-inβœ… Built-in (Modal)❌ Requires Composition
Aspect RatioFixed ContainerViewport FittedCalculated (Fluid)
DependenciesNone (Self-contained)NoneNone (but needs lightbox)
Best ForProduct Sliders, Simple PortfoliosCustom Grids + ModalPinterest-style Layouts

πŸ’‘ The Big Picture

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.

How to Choose: react-image-gallery vs react-images vs react-photo-gallery

  • react-image-gallery:

    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.

  • react-images:

    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.

  • react-photo-gallery:

    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.

README for react-image-gallery

React Image Gallery

A responsive, customizable image gallery component for React


npm version Download Count Bundle size CI TypeScript License: MIT


▢️ VIEW LIVE DEMO


React Image Gallery Demo


✨ Features

FeatureDescription
πŸ“± Mobile SwipeNative touch gestures for smooth mobile navigation
πŸ–ΌοΈ ThumbnailsCustomizable thumbnail navigation with multiple positions
πŸ“Ί FullscreenBrowser fullscreen or CSS-based fullscreen modes
🎨 ThemingCSS custom properties for easy styling
⌨️ Keyboard NavArrow keys, escape, and custom key bindings
πŸ”„ RTL SupportRight-to-left language support
↕️ Vertical ModeSlide vertically instead of horizontally
🎬 Custom SlidesRender videos, iframes, or any custom content

πŸš€ Getting Started

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


βš™οΈ Props

  • items: (required) Array of objects. Available properties:
    • original - image source URL
    • thumbnail - thumbnail source URL
    • fullscreen - 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 class
    • thumbnailClass - custom thumbnail class
    • renderItem - Function for custom rendering a specific slide (see renderItem below)
    • renderThumbInner - Function for custom thumbnail renderer (see renderThumbInner below)
    • originalAlt - image alt
    • thumbnailAlt - thumbnail image alt
    • originalTitle - image title
    • thumbnailTitle - thumbnail image title
    • thumbnailLabel - label for thumbnail
    • description - description for image
    • srcSet - image srcset (html5 attribute)
    • sizes - image sizes (html5 attribute)
    • bulletClass - extra class for the bullet of the item
  • infinite: Boolean, default true - loop infinitely
  • lazyLoad: Boolean, default false
  • showNav: Boolean, default true
  • showThumbnails: Boolean, default true
  • thumbnailPosition: String, default bottom - options: top, right, bottom, left
  • showFullscreenButton: Boolean, default true
  • useBrowserFullscreen: Boolean, default true - if false, uses CSS-based fullscreen
  • useTranslate3D: Boolean, default true - if false, uses translate instead of translate3d
  • showPlayButton: Boolean, default true
  • isRTL: Boolean, default false - right-to-left mode
  • showBullets: Boolean, default false
  • maxBullets: Number, default undefined - max bullets shown (minimum 3, active bullet stays centered)
  • showIndex: Boolean, default false
  • autoPlay: Boolean, default false
  • disableThumbnailScroll: Boolean, default false - disable thumbnail auto-scroll
  • disableKeyDown: Boolean, default false - disable keyboard navigation
  • disableSwipe: Boolean, default false
  • disableThumbnailSwipe: Boolean, default false
  • onErrorImageURL: String, default undefined - fallback image URL for failed loads
  • indexSeparator: String, default ' / ', ignored if showIndex is false
  • slideDuration: Number, default 550 - slide transition duration (ms)
  • swipingTransitionDuration: Number, default 0 - transition duration while swiping (ms)
  • slideInterval: Number, default 3000
  • slideOnThumbnailOver: Boolean, default false
  • slideVertically: Boolean, default false - slide vertically instead of horizontally
  • flickThreshold: Number, default 0.4 - swipe velocity threshold (lower = more sensitive)
  • swipeThreshold: Number, default 30 - percentage of slide width needed to trigger navigation
  • stopPropagation: Boolean, default false - call stopPropagation on swipe events
  • startIndex: Number, default 0
  • onImageError: Function, callback(event) - overrides onErrorImageURL
  • onThumbnailError: Function, callback(event) - overrides onErrorImageURL
  • onThumbnailClick: 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 slide
  • onTouchEnd: Function, callback(event) on gallery slide
  • onTouchStart: Function, callback(event) on gallery slide
  • onMouseOver: Function, callback(event) on gallery slide
  • onMouseLeave: Function, callback(event) on gallery slide
  • additionalClass: String, additional class for the root node
  • renderCustomControls: Function, render custom controls on the current slide
  • renderItem: Function, custom slide rendering
  • renderThumbInner: Function, custom thumbnail rendering
  • renderLeftNav: Function, custom left nav component
  • renderRightNav: Function, custom right nav component
  • renderTopNav: Function, custom top nav component (vertical mode)
  • renderBottomNav: Function, custom bottom nav component (vertical mode)
  • renderPlayPauseButton: Function, custom play/pause button
  • renderFullscreenButton: Function, custom fullscreen button
  • useWindowKeyDown: Boolean, default true - use window or element for key events

πŸ”§ Functions

The following functions can be accessed using refs

  • play(): starts the slideshow
  • pause(): pauses the slideshow
  • togglePlay(): toggles between play and pause
  • fullScreen(): enters fullscreen mode
  • exitFullScreen(): exits fullscreen mode
  • toggleFullScreen(): toggles fullscreen mode
  • slideToIndex(index): slides to a specific index
  • getCurrentIndex(): returns the current index

🀝 Contributing

Pull 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.

  • Follow the eslint config
  • Comment your code

πŸ› οΈ Build the example locally

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.


πŸ“„ License

MIT Β© Xiao Lin