React Native Image Viewer
A lightweight React Native component for swipeable, pinch-to-zoom image galleries with pan gestures, save-to-camera-roll, and fully customizable header/footer/indicator UI.
Repository Health
Technical Analysis
react-native-image-zoom-viewer renders a full-screen, swipeable gallery of images on top of a React Native app, combining horizontal paging with pinch-to-zoom and pan gestures via its companion react-native-image-pan-zoom engine. It ships as a single ImageViewer component that takes an array of image URLs (or local require() sources) and manages loading states, image sizing, and gesture-driven transitions internally, so consumers only need to supply an array of {url, width, height} objects and wrap the component in a Modal.
Every visual element — header, footer, page indicator, arrow buttons, loading placeholder, and the long-press save-to-camera-roll menu — is overridable through render props, making it a common base layer for custom lightbox/gallery UIs rather than a fixed, opinionated modal. It has long been a familiar (if now largely unmaintained) way to add a native-feeling image viewer to React Native apps without pulling in a full media library.
What You Get
- A drop-in
<ImageViewer imageUrls={...} />component for full-screen image galleries in React Native - Built-in pinch-to-zoom and pan gestures via the react-native-image-pan-zoom dependency
- Swipe-left/right paging between images with a configurable flip threshold and page animation time
- A long-press menu for saving the current image to the device camera roll
- Render props for the header, footer, page indicator, arrows, loading state, and failed-image placeholder
- An optional swipe-down-to-dismiss gesture with a configurable threshold
Common Use Cases
- Building a photo gallery or lightbox for a social or e-commerce app’s product images
- Letting a user tap a chat thumbnail to view the full-resolution image with pinch-to-zoom
- Adding a pinch-to-zoom preview to a camera roll or media picker screen
- Offering a save-to-device action on long-press for user-generated or shared images
Under The Hood
Architecture
ImageViewer is implemented as a single class component (src/image-viewer.component.tsx) that owns all interaction state itself — the visible index, a per-image loading/success/fail size cache, and menu visibility — mixed with instance fields for gesture math (an Animated.Value tracking horizontal position, plus running offsets for the current page). Props and state are modeled as classes with default field values in image-viewer.type.ts rather than plain interfaces, an unconventional but internally consistent typing choice. Visual theming is factored out into a separate style-factory module (image-viewer.style.ts) parameterized by measured width, height, and background color, but the component otherwise has no separation between rendering, gesture-response logic, and state management — paging math (goBack/goNext/handleHorizontalOuterRangeOffset) lives directly on the component and calls straight into the external react-native-image-pan-zoom package’s ImageZoom wrapper for the actual pinch/pan capture, so the whole interaction model is tightly coupled to that one dependency’s API.
Tech Stack
Written in TypeScript and compiled with plain tsc (no bundler or Babel pipeline of its own — prepare just recompiles src to built/). The only runtime dependency is react-native-image-pan-zoom, which does the actual pinch-to-zoom and pan gesture capture; react and react-native are loose peer dependencies with no pinned version range. Linting uses the now-deprecated tslint with a shared config rather than ESLint, and there’s no CI workflow in the repository — a demo/ app scaffolded with create-react-native-app exists for manual testing but isn’t wired into any automated pipeline.
Code Quality
There are no real unit tests for the component’s own logic — the only test-shaped file is a boilerplate placeholder inside the demo app, unrelated to the gesture/paging code. Error handling is limited to a single fallback path around image-size resolution; elsewhere, callback props are invoked with TypeScript’s non-null assertion operator rather than guarded checks, and there are a few any casts around native image APIs. Method-level comments are extensive but written in Chinese throughout the main component file, which limits readability for contributors who don’t read the language, and there is no CI or automated lint enforcement visible in the repository.
API Design
The public surface is a single component with one required prop (an array of image objects) and a long tail of optional props, so a minimal working gallery needs little more than that array and a wrapping Modal — further customization of the header, footer, page indicator, arrows, and image element itself is opt-in through render* props rather than style overrides, which keeps initial integration lightweight. Naming is fairly consistent (render* for slot components, on* for callbacks), but the README’s props table is the only reference documentation, with no dedicated docs site, and the exported TypeScript types follow the same class-with-defaults pattern noted above rather than a more idiomatic interface-plus-defaultProps split.