Installation
Requirements
Section titled “Requirements”React 18 or 19, and a browser. The published JavaScript targets ES2020 and
relies on <dialog>.showModal(), Pointer Events, and ResizeObserver,
which puts the floor at Chrome 80, Firefox 98, and Safari 15.4. Importing
the preset stylesheet raises that to Chrome 111, Firefox 113, and Safari
16.2 — the toolbar surface uses CSS color-mix(). Compose your own UI from
react-img-view/primitives and the lower floor applies.
Server rendering is fine; nothing runs until the viewer actually mounts. See Server rendering and Next.js below.
Server rendering and Next.js
Section titled “Server rendering and Next.js”The React entries are published with a "use client" banner, so importing
them inside a Server Component tree is enough — Next.js treats the import
as a client boundary, and you do not need a wrapper module of your own.
Nothing reaches document during render. The portal target (container,
defaulting to document.body) is read only after the viewer has mounted
and opened, so a server render emits no dialog markup at all.
Labels are the one thing to set deliberately. Group falls back to fixed
English strings rather than reading navigator.language, so server and
hydrated markup always agree. Choose another language explicitly, with a
value that is identical on both sides:
import { ImageView } from 'react-img-view'import zhCN from 'react-img-view/locales/zh-CN'import 'react-img-view/styles.css'
export default function Gallery({ images }: { images: { src: string; name: string }[] }) { return <ImageView.Group images={images} labels={zhCN} />}Deriving labels from something only the browser knows — the user agent’s
language, a value in localStorage — renders different text on the two
sides and React will warn about it. Read the choice from the request
instead: a cookie, or a locale segment in the route.
Install
Section titled “Install”npm install react-img-viewpnpm add react-img-viewThe package has no runtime dependencies of its own — the gesture engine, the animation drivers, and the modal are built on nothing but React and the DOM.
Styles
Section titled “Styles”The primitive entry ships without the preset; the main entry supplies the default composition, and the stylesheet supplies its look. There are three ways to style it.
The shipped preset
Section titled “The shipped preset”import 'react-img-view/styles.css'Everything is driven by CSS custom properties, set on :root and on the
dialog itself — never on an ancestor class, since the viewer portals out of
the tree and a class-scoped rule would not follow it.
:root { --riv-accent: #0f766e; --riv-chrome: #ffffff; --riv-stage: #f4f4f5;}Dark mode follows prefers-color-scheme automatically; force one side with
data-riv-theme="light" or data-riv-theme="dark" on :root or the dialog.
See Customization for the full list of properties.
The shadcn registry block
Section titled “The shadcn registry block”If the project already uses Tailwind and shadcn’s conventions, install the Tailwind-classed version instead — real source in your repo, editable like any other component:
npx shadcn add https://baron04.github.io/react-img-view/r/image-view.jsonThe block is the same design shipped another way: the CSS preset uses custom
properties, the block uses Tailwind utility classes. Only the presentation
layer is copied into your project — behaviour (gestures, the modal, keyboard,
the open/close animation) stays a real react-img-view dependency, the same
split shadcn’s own blocks use for Radix.
Neither: styling from scratch
Section titled “Neither: styling from scratch”Every part exposes stable data-* hooks. Controls and layout regions take
asChild, while ImageView always reuses its only child. That is enough to
style from scratch with attribute selectors, Tailwind’s data-[active]:…
variants, or your own design system’s components. See Customization.