react-native-image-pan-zoom vs react-native-image-zoom-viewer
Image Zoom and Pan Interactions in React Native
react-native-image-pan-zoomreact-native-image-zoom-viewer

Image Zoom and Pan Interactions in React Native

react-native-image-pan-zoom and react-native-image-zoom-viewer are both React Native libraries designed to handle image manipulation gestures like pinch-to-zoom, pan, and double-tap zoom. react-native-image-pan-zoom focuses on providing a low-level component that wraps images to enable gesture handling, often giving developers more control over the transformation matrix and animation physics. react-native-image-zoom-viewer is a higher-level solution that often includes a viewer interface with support for multiple images, saving to gallery, and built-in loading states, aiming for a quicker implementation of gallery-like experiences.

Npm Package Weekly Downloads Trend

3 Years

Github Stars Ranking

Stat Detail

Package
Downloads
Stars
Size
Issues
Publish
License
react-native-image-pan-zoom0638-736 years agoISC
react-native-image-zoom-viewer02,445117 kB194-MIT

Image Zoom and Pan Interactions in React Native: Deep Dive

When building mobile apps that involve media, users expect smooth pinch-to-zoom and pan gestures. Both react-native-image-pan-zoom and react-native-image-zoom-viewer solve this, but they target different layers of the application architecture. One acts as a primitive building block, while the other functions as a complete solution.

πŸ—οΈ Architecture: Primitive Component vs Full Viewer

react-native-image-pan-zoom provides a wrapper component that makes its children zoomable.

  • It does not assume the child is an image; it could be a map, a canvas, or a complex view.
  • You are responsible for managing the image source and layout yourself.
// react-native-image-pan-zoom: Wraps any content
import ImageZoom from 'react-native-image-pan-zoom';
import { Image } from 'react-native';

const CustomZoom = () => (
  <ImageZoom cropWidth={Dimensions.get('window').width} cropHeight={Dimensions.get('window').height}>
    <Image source={{ uri: 'https://example.com/img.jpg' }} style={{ width: 300, height: 300 }} />
  </ImageZoom>
);

react-native-image-zoom-viewer is designed specifically for viewing images in a modal or full-screen context.

  • It manages the image loading state internally.
  • It expects an array of image objects, making it ready for galleries immediately.
// react-native-image-zoom-viewer: Dedicated image viewer
import ImageViewer from 'react-native-image-zoom-viewer';

const GalleryViewer = () => {
  const images = [{ url: 'https://example.com/img1.jpg' }, { url: 'https://example.com/img2.jpg' }];
  return <ImageViewer imageUrls={images} />;
};

πŸ–οΈ Gesture Handling: Raw Events vs Built-in Logic

react-native-image-pan-zoom exposes callbacks for gesture events, allowing you to hook into the pan and zoom lifecycle.

  • You can access the current scale and position via callbacks.
  • Useful if you need to sync other UI elements (like a zoom level indicator) with the image state.
// react-native-image-pan-zoom: Access gesture state
<ImageZoom
  onMove={(data) => console.log('Pan position:', data.moveX, data.moveY)}
  onZoom={(data) => console.log('Current scale:', data.scale)}
>
  <Image source={...} />
</ImageZoom>

react-native-image-zoom-viewer abstracts gesture logic to provide a polished UX out of the box.

  • It handles double-tap to zoom and pinch gestures internally without requiring configuration.
  • Custom gesture logic is harder to inject because the component manages the interaction loop.
// react-native-image-zoom-viewer: Limited gesture hooks
<ImageViewer
  onSwipeDown={() => console.log('Swipe down to close detected')}
  onChange={(index) => console.log('Image index changed:', index)}
  // No direct access to raw pinch scale values during gesture
/>

πŸ–ΌοΈ Multiple Image Support: Manual vs Native

react-native-image-pan-zoom handles one content child at a time.

  • To create a gallery, you must build the swipe logic and state management yourself.
  • This adds development time but allows for custom transitions between images.
// react-native-image-pan-zoom: Manual gallery implementation
const [currentIndex, setCurrentIndex] = useState(0);

<ImageZoom>
  <Image source={images[currentIndex]} />
</ImageZoom>
// You must implement Swipeable or FlatList logic externally to change currentIndex

react-native-image-zoom-viewer supports multiple images natively via the imageUrls prop.

  • Swiping left or right automatically transitions to the next image.
  • It includes built-in indicators for the current image position.
// react-native-image-zoom-viewer: Native gallery support
const images = [
  { url: 'https://example.com/1.jpg' },
  { url: 'https://example.com/2.jpg' }
];

<ImageViewer imageUrls={images} index={0} />;
// Swiping is handled internally

βš™οΈ Configuration and Customization

react-native-image-pan-zoom requires explicit definition of the crop area.

  • You must pass cropWidth and cropHeight usually derived from window dimensions.
  • Failing to update these on rotation can lead to layout bugs.
// react-native-image-pan-zoom: Explicit dimensions required
const { width, height } = Dimensions.get('window');

<ImageZoom cropWidth={width} cropHeight={height} imageWidth={300} imageHeight={300}>
  <Image source={...} />
</ImageZoom>

react-native-image-zoom-viewer attempts to auto-detect dimensions but allows overrides.

  • It is more forgiving with layout props but can be harder to constrain within a partial screen area.
  • Best used in a modal or full-screen container where it controls the viewport.
// react-native-image-zoom-viewer: Flexible but opinionated layout
<ImageViewer
  imageUrls={images}
  useSwipeNum={2} // Configure swipe sensitivity
  enableSwipeDown={true} // Enable dismiss gesture
/>

πŸ“‰ Maintenance and Ecosystem Reality

react-native-image-pan-zoom has historically been stable but sees infrequent updates.

  • It relies on core React Native gesture responders which have remained consistent.
  • Suitable for long-term projects where the API surface does not need to change.

react-native-image-zoom-viewer has faced maintenance challenges in the past with React Native version upgrades.

  • Some forks exist to support newer React Native versions if the main package lags.
  • Developers should verify compatibility with their specific React Native version before committing.

🀝 Similarities: Shared Capabilities

Despite their architectural differences, both libraries solve the core problem of touch interaction for media.

1. πŸ“Œ Pinch-to-Zoom Support

  • Both enable multi-touch scaling.
  • Both respect minimum and maximum zoom limits.
// react-native-image-pan-zoom
<ImageZoom maxOverflow={100} minScale={0.5} maxScale={3}>
  <Image source={...} />
</ImageZoom>

// react-native-image-zoom-viewer
<ImageViewer maxScale={4} minScale={1} imageUrls={...} />

2. πŸ“± Mobile Gesture Standards

  • Both support double-tap to toggle zoom.
  • Both prevent scroll conflicts when zoomed in.
// react-native-image-pan-zoom
<ImageZoom enableDoubleClickZoom={true} />

// react-native-image-zoom-viewer
<ImageViewer enableDoubleClickZoom={true} />

3. 🎨 Custom Rendering

  • Both allow some level of custom rendering for loading or error states.
// react-native-image-pan-zoom
// Render custom loader inside the Image component before source loads
<Image source={...} onLoadStart={() => setLoading(true)} />

// react-native-image-zoom-viewer
<ImageViewer
  renderImage={(props) => <CustomImageComponent {...props} />}
  renderIndicator={() => <CustomIndicator />}
/>

πŸ“Š Summary: Key Differences

Featurereact-native-image-pan-zoomreact-native-image-zoom-viewer
Primary UseEmbeddable zoomable areaFull-screen image gallery
Content TypeAny View (Image, Map, SVG)Images only
Gallery LogicManual implementation requiredBuilt-in swipe navigation
Layout ControlHigh (define crop area)Low (expects full viewport)
Setup ComplexityHigher (more props to configure)Lower (pass array of URLs)

πŸ’‘ The Big Picture

react-native-image-pan-zoom is like a raw engine πŸŽοΈβ€”it gives you the power to build a custom driving experience. Use it when the image is just one part of a complex screen, like a product detail page where you also need to interact with buttons and text around the zoomable area.

react-native-image-zoom-viewer is like a rideshare service πŸš•β€”it gets you from A to B with minimal effort. Use it when you need a standard photo gallery quickly and don't want to maintain custom gesture logic.

Final Thought: If you are building a dedicated photo app, react-native-image-zoom-viewer saves weeks of work. If you are building a dashboard with zoomable charts or maps, react-native-image-pan-zoom is the only viable option.

How to Choose: react-native-image-pan-zoom vs react-native-image-zoom-viewer

  • react-native-image-pan-zoom:

    Choose react-native-image-pan-zoom if you need fine-grained control over gesture physics and transformation matrices within a specific layout. It is better suited for scenarios where the image is part of a larger interactive interface, such as a map annotation or a custom editor, rather than a full-screen gallery. This package is ideal when you want to build custom UI around the zoomable area without being locked into a predefined viewer structure.

  • react-native-image-zoom-viewer:

    Choose react-native-image-zoom-viewer if your goal is to implement a full-screen image gallery with minimal setup. It is the preferred choice for standard photo viewing workflows where features like swipe navigation between multiple images, saving to the device, and built-in loading indicators are required out of the box. Use this when development speed and standard user experience patterns take priority over custom gesture tuning.

README for react-native-image-pan-zoom

Show Cases

All Contributors

Zoom while sliding

3.gif

Intelligent zoom

2.gif

Getting Started

Installation

npm i react-native-image-pan-zoom --save

Basic Usage

  • Install create-react-native-app first
$ npm install -g create-react-native-app
  • Initialization of a react-native project
$ create-react-native-app AwesomeProject
  • Then, edit AwesomeProject/App.js, like this:
import { Image, Dimensions } from 'react-native';
import ImageZoom from 'react-native-image-pan-zoom';

export default class App extends React.Component {
    render: function() {
        return (
            <ImageZoom cropWidth={Dimensions.get('window').width}
                       cropHeight={Dimensions.get('window').height}
                       imageWidth={200}
                       imageHeight={200}>
                <Image style={{width:200, height:200}}
                       source={{uri:'http://v1.qzone.cc/avatar/201407/07/00/24/53b9782c444ca987.jpg!200x200.jpg'}}/>
            </ImageZoom>
        )
    }
}

Document

PropsTypeDescriptionDefaultValue
cropWidth(required)numberoperating area width100
cropHeight(required)numberoperating area height100
imageWidth(required)numberpicture width100
imageHeight(required)numberpicture height100
onClick(eventParams: IOnClick)=>voidonClick()=>{}
onDoubleClick(eventParams: IOnClick)=>voidonDoubleClick()=>{}
panToMovebooleanallow to move picture with one fingertrue
pinchToZoombooleanallow scale with two fingerstrue
clickDistancenumberhow many finger movement can also trigger onClick10
horizontalOuterRangeOffset(offsetX?: number)=>voidhorizontal beyond the distance, the parent to do picture switching, you can listen to this function. When this function is triggered, you can do the switch operation()=>{}
onDragLeft()=>voidtrigger to switch to the left of the graph, the left sliding speed exceeds the threshold when triggered()=>{}
responderRelease(vx: number)=>voidlet go but do not cancel()=>{}
maxOverflownumbermaximum sliding threshold100
longPressTimenumberlong press threshold800
onLongPress(eventParams: IOnClick)=>voidon longPress()=> {}
doubleClickIntervalnumbertime allocated for second click to be considered as doublClick event175
onMove( position: IOnMove )=>voidreports movement position data (helpful to build overlays)()=> {}
centerOn{ x: number, y: number, scale: number, duration: number }if given this will cause the map to pan and zoom to the desired locationundefined
enableSwipeDownbooleanfor enabling vertical movement if user doesn't want itfalse
enableCenterFocusbooleanfor disabling focus on image center if user doesn't want ittrue
onSwipeDown() => voidfunction that fires when user swipes downnull
swipeDownThresholdnumberthreshold for firing swipe down function230
minScalenumberminimum zoom scale0.6
maxScalenumbermaximum zoom scale10
useNativeDriverbooleanWhether to animate using useNativeDriverfalse
onStartShouldSetPanResponder() => booleanOverride onStartShouldSetPanResponder behavior() => true
onMoveShouldSetPanResponder() => booleanOverride onMoveShouldSetPanResponder behaviorundefined
onPanResponderTerminationRequest() => booleanOverride onMoveShouldSetPanResponder behavior() => false
useHardwareTextureAndroidbooleanfor disabling rendering to hardware texture on Androidtrue
MethodparamsDescription
resetReset the position and the scale of the image
resetScaleReset the scale of the image
centerOnICenterOnCenters the image in the position indicated. ICenterOn={ x: number, y: number, scale: number, duration: number }

Development pattern

Step 1, run TS listener

After clone this repo, then:

npm install
npm start

Step 2, run demo

cd demo
npm install
npm start

Then, scan the QR, use your expo app.

Contributors ✨

Thanks goes to these wonderful people (emoji key):


Darius

πŸ’»

Thomas P.

πŸ’»

Juan Di Toro

πŸ’»

Alhaytham Elhassan

πŸ’»

Alexander Pataridze

πŸ’»

Peter Xu

πŸ’»

This project follows the all-contributors specification. Contributions of any kind welcome!