react-image-gallery vs react-image-lightbox vs react-images vs react-photo-gallery
Architectural Comparison of React Image Gallery and Lightbox Libraries
react-image-galleryreact-image-lightboxreact-imagesreact-photo-gallery

Architectural Comparison of React Image Gallery and Lightbox Libraries

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.

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-image-lightbox01,278-05 years agoMIT
react-images02,335-505 years agoMIT
react-photo-gallery02,014-807 years agoMIT

Architectural Comparison of React Image Gallery and Lightbox Libraries

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.

πŸ—‚οΈ Layout Engines: Sliders vs. Masonry Grids

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

πŸ” Interaction Models: Built-in vs. Composed

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

🎨 Customization and Styling

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

⚠️ Maintenance Status and Modern React Compatibility

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.

🌐 Real-World Scenarios

Scenario 1: E-Commerce Product Page

You need a main product image, thumbnails below, and a zoom/fullscreen feature. You don't want to build this from scratch.

  • βœ… Best choice: react-image-gallery
  • Why? It solves the exact "product carousel" pattern out of the box with accessible keyboard nav and swipe.
<ImageGallery items={productImages} showThumbnails={true} showFullscreenButton={true} />

Scenario 2: Photography Portfolio with Unique Design

You need a masonry grid that looks perfect, and when clicked, opens a custom modal with your brand's typography and transitions.

  • βœ… Best choice: react-photo-gallery + react-images
  • Why? Use react-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={{...}} />

Scenario 3: Legacy Admin Dashboard

You have an existing app using React 16, and you just need a quick way to click an avatar and see it larger.

  • ⚠️ Choice: react-image-lightbox (Only if migration is impossible)
  • Why? It's lightweight and was easy to drop in, but you should plan to replace it soon.
// Legacy pattern
{showLightbox && <Lightbox mainSrc={avatar} onCloseRequest={() => setShow(false)} />}

πŸ“Š Summary Table

Featurereact-image-galleryreact-image-lightboxreact-imagesreact-photo-gallery
Primary RoleAll-in-one SliderSimple ModalCustomizable ViewerLayout Grid
Layout TypeLinear / CarouselNone (Overlay)Flexible / GridMasonry / Justified
CustomizationMedium (CSS overrides)Low (Hardcoded)High (Component injection)High (Render props)
Maintenanceβœ… Active❌ Deprecatedβœ… Activeβœ… Active
React 18 Readyβœ… Yes❌ Noβœ… Yesβœ… Yes
Best ForProduct CarouselsLegacy Quick FixesBranded ExperiencesPhoto Grids

πŸ’‘ Final Recommendation

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.

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

  • react-image-gallery:

    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.

  • react-image-lightbox:

    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.

  • react-images:

    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.

  • react-photo-gallery:

    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.

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