react-viewer is a plain React image viewer with an unusually wide prop surface
react image viewer, supports rotation, scale, zoom and so on
At a glance
- What is it?
- react-viewer is a React image viewer written to replace viewerjs inside React applications, offering rotation, zoom, drag, download and a filterable toolbar. It is a client only component, and the 3.x line no longer supports direct server rendering.
- Who is it for?
- react-viewer suits a React team that needs an image lightbox with rotation, zoom and a toolbar it can prune down, and it fits a Next.js App Router project that is willing to use a dynamic import with ssr disabled. It does not fit an application that renders the viewer on the server, since direct server rendering is unsupported in 3.x, nor a team that wants a component library with a documented accessibility and focus management story, which the prop table does not cover.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 37 days ago.
- What is it written in?
- Mainly TypeScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 24, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Written to replace viewerjs inside a React tree
The origin story is narrow and useful. The author states that using viewerjs in React was uncomfortable, so react-viewer was created to replace it. That framing tells you what the component is and is not. It is a lightbox for a React application, not a standalone gallery script that you attach to any page, and not a general purpose image gallery with thumbnails, captions and lazy loading. The package description is simply a react image viewer, and the topics attached to the repository point the same way: gallery, image, photo, viewer, React component. If your need is a full gallery experience with a strip of thumbnails, this component is not that. If your need is a modal that opens over your existing layout, zooms, rotates and lets the user step through a set of images, it is exactly that.
Installation is one command, mounting is three props
The stated requirement is react >= 16.8.0 and react-dom >= 16.8.0, which in practice means the hooks API must be available in your application. Installation is the usual npm line:
npm install react-viewer --saveThe usage example that the documentation gives is deliberately small. A visible boolean and an onClose callback control the modal, and an images array carries the sources:
import * as React from 'react';
import Viewer from 'react-viewer';
function App() {
const [ visible, setVisible ] = React.useState(false);
return (
<div>
<button onClick={() => { setVisible(true); } }>show</button>
<Viewer
visible={visible}
onClose={() => { setVisible(false); } }
images={[{src: '', alt: ''}]}
/>
</div>
);
}Only visible, onClose and images are required. Everything else in the prop table has a default, and the presence of about thirty optional props is the main design decision in this library.
Next.js needs the dynamic import or the build breaks
react-viewer uses browser DOM APIs, so the documentation states it must be loaded with SSR disabled. In the Next.js App Router the import has to happen inside a Client Component, with the ssr option turned off:
'use client';
import dynamic from 'next/dynamic';
const ReactViewer = dynamic(
() => import('react-viewer').then((mod) => mod.default),
{ ssr: false },
);
export default function Viewer() {
return (
<ReactViewer
visible={true}
onClose={() => {}}
images={[{src: ''}]}
/>
);
}The Pages Router uses the same dynamic import but does not need the 'use client' directive. The blunt fact underneath is that direct server rendering is not supported in 3.x, which is a breaking change for anyone upgrading an application that relied on the older behaviour. The repository keeps a separate ssr-test project for exactly this reason, and the package manifest has a check:ssr-hydration script that runs against it.
Toolbar filtering keys off stable names, not action codes
The customToolbar prop takes a function that receives the default toolbar configurations and returns a new array. The documentation is specific about how to write it: filter the configurations by the key value, and the key values are stable and avoid depending on numeric actionType values. The example keeps only the zoom controls:
<Viewer
visible={visible}
onClose={() => setVisible(false)}
images={images}
customToolbar={(toolbars) =>
toolbars.filter(({ key }) => key === 'zoomIn' || key === 'zoomOut')
}
/>The returned array also determines the left to right order of the buttons, so a custom toolbar can also reorder the built in controls while keeping their original click behaviour. A ToolbarConfig entry has a key, an optional render node and an optional onClick callback, which is enough to replace a button with your own icon while leaving the rest of the viewer intact.
Every part of the chrome can be removed one prop at a time
The prop table is where the library's personality shows. Booleans exist to strip the interface apart: noClose stops the close button being rendered, noNavbar removes the navbar, noToolbar removes the toolbar, noImgDetails suppresses the width and height readout, and noFooter removes the footer entirely. Changeable controls the change buttons, attribute toggles the image attribute display, and downloadable, which defaults to false, adds a download action, with downloadInNewWindow deciding where that download happens and exportFileName, defaulting to exportFile, naming the file. Behavioural defaults are equally opinionated: zoomable, rotatable and scalable are all on, loop is on so the image set wraps, minScale is 0.1, zoomSpeed is 0.05, showTotal is on with the separator totalName set to of, and zIndex defaults to 1000. Keyboard support exists and can be turned off with disableKeyboardSupport, and mouse zoom can be disabled separately with disableMouseZoom.
Container, fallback image and size limits fill the remaining gaps
Three smaller mechanisms round out the component. The container prop sets the parent node in inline mode, which is how the viewer is mounted into an existing element instead of the document body, and it defaults to null. The defaultImg prop is a fallback shown when an image fails to load, taking a src plus optional width and height, and the images array entries carry the same shape through the ImageDecorator type with an added downloadUrl. Size handling has two escape hatches: defaultSize sets the default image size through a ViewerImageSize of width and height, and noLimitInitializationSize removes the limit on the image size used at initialization. Zoom state across image changes is controlled by noResetZoomAfterChange, which preserves the zoom level when the active image changes, while onChange reports the active image and its index to the caller.
A verification script guards what the package publishes
The package manifest for version 3.2.6 shows a release process that is more disciplined than most component libraries. The verify script chains the pieces together:
"verify": "npm test -- --runInBand && npm run lint && npm run build && npm run check:package-export && npm run doc && npm run build:ssr && npm run check:ssr-hydration && npm run check:multipart && npm run audit:critical && npm pack --dry-run",Alongside it sit separate scripts for the package export check, the multipart check, an SSR hydration check and a critical level audit, plus jest for tests, tslint for linting and webpack for the build, the demo bundle and the documentation bundle. The published package contains only dist and lib, with typings at lib/index.d.ts. The licence is MIT, and the release history shows v3.2.6 on 2026-09-18, v3.2.5 on 2026-09-01 and v3.2.4 on 2026-08-21, with the last push also on 2026-09-18. A library that ships three releases in a month is easy to upgrade, but it also means the minor version numbers are where behaviour changes hide, as the SSR removal in 3.x shows.
Editorial conclusion
react-viewer suits a React team that needs an image lightbox with rotation, zoom and a toolbar it can prune down, and it fits a Next.js App Router project that is willing to use a dynamic import with ssr disabled. It does not fit an application that renders the viewer on the server, since direct server rendering is unsupported in 3.x, nor a team that wants a component library with a documented accessibility and focus management story, which the prop table does not cover. Before adopting it, run the ssr-test project that the package manifest references, check the keyboard support behaviour against your own accessibility requirements, and confirm the version you install is the 3.x line rather than the older server rendering friendly releases.
Frequently asked questions
How do I install react-viewer in a React project?
Run npm install react-viewer --save. The documented requirement is react >= 16.8.0 and react-dom >= 16.8.0, so your application must have the hooks API available.
Does react-viewer work with Next.js server side rendering?
No. react-viewer uses browser DOM APIs and must be loaded with SSR disabled, using a dynamic import with the ssr option set to false. Direct server rendering is not supported in 3.x, and the Pages Router needs the same dynamic import without the 'use client' directive.
How do I keep only some buttons in the react-viewer toolbar?
Pass a function to customToolbar that filters the default configurations by their key values, for example keeping only 'zoomIn' and 'zoomOut'. The documentation notes that key values are stable and that the returned array also sets the left to right order.
Can I remove the close button or the footer from react-viewer?
Yes, through individual boolean props: noClose, noNavbar, noToolbar, noImgDetails and noFooter each remove a specific part of the interface. The download button is opt in through the downloadable prop, which defaults to false.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/infeng-react-viewer)Was this useful?