---
url: /1.x/guide/getting-started/overview.md
---
# Overview

Have you ever struggled with implementing complex gesture handling and animations when building image galleries or content viewers in React Native?
Existing libraries often have limited customization options or performance issues. `react-native-gesture-image-viewer` is a high-performance **universal gesture viewer** library built on [**React Native Reanimated**](https://docs.swmansion.com/react-native-reanimated/docs/3.x/fundamentals/getting-started/) and [**Gesture Handler**](https://docs.swmansion.com/react-native-gesture-handler/docs/), providing complete customization and intuitive gesture support for not only images but also videos, custom components, and any other content.
## Key Features
- π€ **Complete Gesture Support** - Pinch zoom, double-tap zoom, swipe navigation, pan when zoomed-in, and vertical drag to dismiss
- ποΈ **High-Performance Animations** - Smooth and responsive animations at 60fps and beyond, powered by React Native Reanimated
- π¨ **Full Customization** - Total control over components, styles, and gesture behavior
- ποΈ **External Control API** - Trigger actions programmatically from buttons or other UI components
- π§© **Multi-Instance Management** - Manage multiple viewers independently using unique IDs
- 𧬠**Flexible Integration** - Use with Modal, [React Native Modal](https://www.npmjs.com/package/react-native-modal), ScrollView, FlatList, [FlashList](https://www.npmjs.com/package/@shopify/flash-list), [Expo Image](https://www.npmjs.com/package/expo-image), [FastImage](https://github.com/DylanVann/react-native-fast-image), and more
- π§ **Full TypeScript Support** - Great developer experience with type inference and safety
- π **Cross-Platform Support** - Runs on iOS, Android, and Web with Expo Go and New Architecture compatibility
- πͺ **Easy-to-Use API** - Simple and intuitive API that requires minimal setup
---
url: /1.x/guide/getting-started/installation.md
---
# Installation
:::warning React Native Reanimated v4 Users
If you're using **React Native Reanimated v4**, **version 2.x** of this library is recommended for the best experience.
- π **[Go to v2.x Documentation](/guide/getting-started/installation.md)**
- π **[Migration Guide from 1.x to 2.x](/guide/migration-from-1.x.md)**
While v1.x may work with Reanimated v4, it's optimized for v3 and you might encounter deprecation warnings or compatibility issues.
:::
:::info Important
`react-native-gesture-image-viewer` is a high-performance viewer library built on [`react-native-reanimated`](https://www.npmjs.com/package/react-native-reanimated) and [`react-native-gesture-handler`](https://www.npmjs.com/package/react-native-gesture-handler).\
Therefore, you **must install** React Native Reanimated and Gesture Handler before using this library. Please refer to the official documentation of these libraries for detailed setup guides.
:::
#### Minimum Requirements
| Library | Minimum Version |
| :----------------------------- | :-------------: |
| `react` | `>=18.0.0` |
| `react-native` | `>=0.75.0` |
| `react-native-gesture-handler` | `>=2.24.0` |
| `react-native-reanimated` | `>=3.0.0` |
## [React Native Reanimated Setup](https://docs.swmansion.com/react-native-reanimated/docs/3.x/fundamentals/getting-started/)
```sh [npm]
npm install react-native-reanimated
```
```sh [yarn]
yarn add react-native-reanimated
```
```sh [pnpm]
pnpm add react-native-reanimated
```
```sh [bun]
bun add react-native-reanimated
```
```sh [deno]
deno add npm:react-native-reanimated
```
### Add the plugin to your `babel.config.js`:
```js title="babel.config.js"
module.exports = {
presets: [
... // don't add it here :)
],
plugins: [
...
// for web
'@babel/plugin-proposal-export-namespace-from', // [!code highlight]
// react-native-reanimated/plugin has to be listed last.
'react-native-reanimated/plugin', // [!code highlight]
],
};
```
### Wrap your Metro config with `wrapWithReanimatedMetroConfig` in `metro.config.js`:
```js title="metro.config.js"
// [!code highlight:3]
const { wrapWithReanimatedMetroConfig } = require('react-native-reanimated/metro-config');
const config = {
// Your existing Metro configuration options
};
module.exports = wrapWithReanimatedMetroConfig(config); // [!code highlight]
```
## [React Native Gesture Handler Setup](https://docs.swmansion.com/react-native-gesture-handler/docs/fundamentals/installation)
```sh [npm]
npm install react-native-gesture-handler
```
```sh [yarn]
yarn add react-native-gesture-handler
```
```sh [pnpm]
pnpm add react-native-gesture-handler
```
```sh [bun]
bun add react-native-gesture-handler
```
```sh [deno]
deno add npm:react-native-gesture-handler
```
- `react-native-gesture-handler` generally doesn't require additional setup, but please refer to the official documentation for your specific environment.
- For [using gestures in Android modals](https://docs.swmansion.com/react-native-gesture-handler/docs/fundamentals/installation#android), you would normally need to wrap modal content with `GestureHandlerRootView`. However, **this library already includes `GestureHandlerRootView` internally, so no additional wrapping is needed when using modals.**
## Install React Native Gesture Image Viewer
Youβre all set! π\
Start by installing `react-native-gesture-image-viewer`
```sh [npm]
npm install react-native-gesture-image-viewer@1.x
```
```sh [yarn]
yarn add react-native-gesture-image-viewer@1.x
```
```sh [pnpm]
pnpm add react-native-gesture-image-viewer@1.x
```
```sh [bun]
bun add react-native-gesture-image-viewer@1.x
```
```sh [deno]
deno add npm:react-native-gesture-image-viewer@1.x
```
---
url: /1.x/guide/getting-started/quick-start.md
---
# Quick Start

## Examples & Demo
- [π Example Project](https://github.com/saseungmin/react-native-gesture-image-viewer/tree/v1.x/example) - Real implementation code with various use cases
- [π€ Expo Go](https://snack.expo.dev/@harang/react-native-gesture-image-viewer) - Try it instantly on Snack Expo
## Installation
:::warning Important
`react-native-gesture-image-viewer` is a high-performance viewer library built on [`react-native-reanimated`](https://www.npmjs.com/package/react-native-reanimated) and [`react-native-gesture-handler`](https://www.npmjs.com/package/react-native-gesture-handler).\
Therefore, you **must install** React Native Reanimated and Gesture Handler before using this library. **If you haven't set it up yet, please refer to the [installation guide](/1.x/guide/getting-started/installation.md).**
:::
```sh [npm]
npm install react-native-gesture-image-viewer@1.x
```
```sh [yarn]
yarn add react-native-gesture-image-viewer@1.x
```
```sh [pnpm]
pnpm add react-native-gesture-image-viewer@1.x
```
```sh [bun]
bun add react-native-gesture-image-viewer@1.x
```
```sh [deno]
deno add npm:react-native-gesture-image-viewer@1.x
```
## Basic Usage
`react-native-gesture-image-viewer` is a library focused purely on gesture interactions for complete customization.
```tsx
import { useCallback, useState } from 'react';
import { ScrollView, Image, Modal, View, Text, Button } from 'react-native';
import { GestureViewer, GestureTrigger, useGestureViewerController, useGestureViewerEvent } from 'react-native-gesture-image-viewer';
function App() {
const images = [...];
const [visible, setVisible] = useState(false);
const [selectedIndex, setSelectedIndex] = useState(0);
const { goToIndex, goToPrevious, goToNext, currentIndex, totalCount } = useGestureViewerController();
const openModal = (index: number) => {
setSelectedIndex(index);
setVisible(true);
};
const renderImage = useCallback((imageUrl: string) => {
return ;
}, []);
useGestureViewerEvent('zoomChange', (data) => {
console.log(`Zoom changed from ${data.previousScale} to ${data.scale}`);
});
return (
{images.map((uri, index) => (
openModal(index)}>
))}
setVisible(false)}
/>
);
}
```
---
url: /1.x/guide/getting-started/ai.md
---
# AI
To help AI better understand this library's `1.x` behavior, documentation, and project conventions so it can provide more accurate help during development and troubleshooting, this project provides the following capabilities:
## llms.txt
[`llms.txt`](https://llmstxt.org/) helps LLMs discover and use project documentation. For `1.x`, use the following files.
- [1.x/llms.txt](https://react-native-gesture-image-viewer.pages.dev/1.x/llms.txt): The structured index file for the `1.x` docs.
```txt
https://react-native-gesture-image-viewer.pages.dev/1.x/llms.txt
```
- [1.x/llms-full.txt](https://react-native-gesture-image-viewer.pages.dev/1.x/llms-full.txt): The full-content file that concatenates the complete `1.x` documentation into a single file.
```txt
https://react-native-gesture-image-viewer.pages.dev/1.x/llms-full.txt
```
## Markdown docs
Every documentation page also has a corresponding Markdown version that can be passed directly to AI.
Examples:
```txt
https://react-native-gesture-image-viewer.pages.dev/1.x/guide/getting-started/overview.md
https://react-native-gesture-image-viewer.pages.dev/1.x/guide/getting-started/quick-start.md
https://react-native-gesture-image-viewer.pages.dev/1.x/guide/usage/programmatic-control.md
```
Providing a single Markdown page is often the most efficient option when you want help with one specific feature.
---
url: /1.x/guide/performance-optimization-tips.md
---
# Performance Optimization Tips
#### Wrap the `renderItem` function with `useCallback` to prevent unnecessary re-renders.
```tsx
import { useCallback, useState } from 'react'; // [!code highlight]
import { ScrollView, Image, Modal } from 'react-native';
import { GestureViewer } from 'react-native-gesture-image-viewer';
function App() {
const images = [...];
const [visible, setVisible] = useState(false);
// [!code highlight:3]
const renderImage = useCallback((imageUrl: string) => {
return ;
}, []);
return (
setVisible(false)}>
setVisible(false)}
/>
);
}
```
#### For large images, we recommend using [`expo-image`](https://docs.expo.dev/versions/latest/sdk/image/) or [`FastImage`](https://github.com/DylanVann/react-native-fast-image).
```tsx
import { ScrollView, Modal } from 'react-native';
import { GestureViewer } from 'react-native-gesture-image-viewer';
import { Image } from 'expo-image'; // [!code highlight]
function App() {
const images = [...];
const [visible, setVisible] = useState(false);
const renderImage = useCallback((imageUrl: string) => {
return ; // [!code highlight]
}, []);
return (
setVisible(false)}>
setVisible(false)}
/>
);
}
```
#### For handling many images, we recommend using [`FlashList`](https://shopify.github.io/flash-list/).
- [FlashList Performance Guide](https://shopify.github.io/flash-list/docs/fundamentals/performance)
```tsx
import { useCallback, useState } from 'react';
import { Image, Modal } from 'react-native';
import { GestureViewer } from 'react-native-gesture-image-viewer';
import { FlashList } from '@shopify/flash-list'; // [!code highlight]
function App() {
const images = [...];
const [visible, setVisible] = useState(false);
const renderImage = useCallback((imageUrl: string) => {
return ;
}, []);
return (
setVisible(false)}>
setVisible(false)}
/>
);
}
```
#### Test on actual devices (performance may be limited in simulators).
---
url: /1.x/guide/usage/basic-usage.md
---
# Basic Usage
## GestureViewer
```tsx
import { FlatList, Image, Modal } from 'react-native';
import { GestureViewer } from 'react-native-gesture-image-viewer';
function App() {
const images = [...];
const [visible, setVisible] = useState(false);
const renderImage = useCallback((imageUrl: string) => {
return ;
}, []);
return (
setVisible(false)}>
setVisible(false)}
/>
);
}
```
## [useGestureViewerController](/1.x/guide/usage/programmatic-control.md)
You can programmatically control the `GestureViewer` using the `useGestureViewerController` hook.
```tsx
import { GestureViewer, useGestureViewerController } from 'react-native-gesture-image-viewer';
function App() {
const {
goToIndex,
goToPrevious,
goToNext,
currentIndex,
totalCount,
zoomIn,
zoomOut,
resetZoom,
rotate,
} = useGestureViewerController();
return (
{/* Navigation Controls */}
goToIndex(2)} />
{`${currentIndex + 1} / ${totalCount}`}
);
}
```
## [useGestureViewerEvent](/1.x/guide/usage/handling-viewer-events.md)
You can subscribe to specific events from `GestureViewer` using the `useGestureViewerEvent` hook. This allows you to respond to real-time gesture changes like zoom and rotation.
```tsx
import { GestureViewer, useGestureViewerEvent } from 'react-native-gesture-image-viewer';
function App() {
useGestureViewerEvent('zoomChange', (data) => {
console.log(`Zoom changed from ${data.previousScale} to ${data.scale}`);
});
useGestureViewerEvent('rotationChange', (data) => {
console.log(`Rotation changed from ${data.previousRotation}Β° to ${data.rotation}Β°`);
});
return ;
}
```
## [GestureTrigger](/1.x/guide/usage/trigger-based-animations.md)
`GestureTrigger` supports smooth trigger-based animations that create seamless transitions from trigger elements (like thumbnails) to the full modal view. This feature enhances user experience by maintaining visual continuity between the trigger and modal content.
```tsx
import { GestureTrigger, GestureViewer } from 'react-native-gesture-image-viewer';
function App() {
const [visible, setVisible] = useState(false);
return (
setVisible(true)}>
setVisible(false)} />
);
}
```
---
url: /1.x/guide/usage/custom-components.md
---
# Custom Components
`react-native-gesture-image-viewer` offers powerful complete component customization. You can create gesture-supported items with not only images but any component you want.
## Modal Components
You can create a viewer using any `Modal` of your choice as shown below:
**Use Modal**
```tsx
import { FlatList, Image, Modal } from 'react-native';
import { GestureViewer } from 'react-native-gesture-image-viewer';
function App() {
const images = [...];
const [visible, setVisible] = useState(false);
return (
setVisible(false)}>
setVisible(false)}
/>
);
}
```
**Use react-nativee-modal**
```tsx
import { FlatList, Image } from 'react-native';
import Modal from 'react-native-modal';
import { GestureViewer } from 'react-native-gesture-image-viewer';
function App() {
const images = [...];
const [visible, setVisible] = useState(false);
return (
setVisible(false)}
onBackdropPress={() => setVisible(false)}
hasBackdrop={false}
style={styles.modal}
useNativeDriver={true}
hideModalContentWhileAnimating={true}
animationInTiming={300}
animationOutTiming={300}
>
setVisible(false)}
/>
);
}
```
## List Components
Support for any list component like `ScrollView`, `FlatList`, [`FlashList`](https://shopify.github.io/flash-list/) through the `ListComponent` prop.\
The `listProps` provides **type inference based on the selected list component**, ensuring accurate autocompletion and type safety in your IDE.
```tsx
import { FlashList } from '@shopify/flash-list';
function App() {
return (
);
}
```
## Content Components
You can inject various types of content components like [`expo-image`](https://docs.expo.dev/versions/latest/sdk/image/), [`FastImage`](https://github.com/DylanVann/react-native-fast-image), etc., through the `renderItem` prop to use gestures.
```tsx
import { GestureViewer } from 'react-native-gesture-image-viewer';
import { Image } from 'expo-image';
function App() {
const renderImage = useCallback((imageUrl: string) => {
return (
);
}, []);
return ;
}
```
---
url: /1.x/guide/usage/gesture-features.md
---
# Gesture Features
`react-native-gesture-image-viewer` supports various gestures essential for viewers. Please refer to the examples below for default gesture actions.
```tsx
import { GestureViewer } from 'react-native-gesture-image-viewer';
function App() {
return (
);
}
```
## Props
### `enableDismissGesture` (default: `true`)
Calls `onDismiss` function when swiping down. Useful for closing modals with downward swipe gestures.
### `enableSwipeGesture` (default: `true`)
Controls left/right swipe gestures. When `false`, horizontal gestures are disabled.
### `enableZoomGesture` (default: `true`)
Controls two-finger pinch gestures with focal point zooming. When `false`, pinch zoom is disabled. Zoom centers on the point between your two fingers for intuitive scaling.
### `enableDoubleTapGesture` (default: `true`)
Controls double-tap zoom gestures with precision targeting. When `false`, double-tap zoom is disabled. Zoom centers exactly on the tapped location.
### `enableZoomPanGesture` (default: `true`)
Enables panning when zoomed in with automatic boundary detection. When `false`, movement is disabled during zoom. Prevents content from moving outside visible screen area.
---
url: /1.x/guide/usage/gesture-viewer-props.md
---
# GestureViewer Props
### `enableLoop` (default: `false`)
Enables loop mode. When `true`, navigating next from the last item goes to the first item, and navigating previous from the first item goes to the last item.
```tsx
import { GestureViewer } from 'react-native-gesture-image-viewer';
function App() {
const [currentIndex, setCurrentIndex] = useState(0);
return ;
}
```
### `onIndexChange`
The `onIndexChange` callback function is called when the `index` value changes.
```tsx
import { GestureViewer } from 'react-native-gesture-image-viewer';
function App() {
const [currentIndex, setCurrentIndex] = useState(0);
return ;
}
```
### `useSnap` (default: `false`)
Sets the scroll behavior mode. `false` (default) uses paging mode, `true` uses snap mode.
```tsx
import { GestureViewer } from 'react-native-gesture-image-viewer';
function App() {
return ;
}
```
### `itemSpacing` (default: `0`)
Sets the spacing between items in pixels. Only applied when `useSnap` is `true`.
```tsx
import { GestureViewer } from 'react-native-gesture-image-viewer';
function App() {
return (
);
}
```
### `autoPlay` (default: `false`)
Enables auto play mode. When `true`, the viewer will automatically play the next item after the specified interval.
- When `enableLoop` is enabled, the viewer will loop back to the first item after the last item.
- When `enableLoop` is disabled, the viewer will stop at the last item.
- When there is only one item, auto-play is disabled.
- When zoom or rotate gestures are detected, the auto-play will be paused.
```tsx
import { GestureViewer } from 'react-native-gesture-image-viewer';
function App() {
return ;
}
```
### `autoPlayInterval` (default: `3000`)
Sets the interval between auto play in milliseconds.\
Must be a positive integer. Values below 250ms are clamped to 250ms at runtime.
```tsx
import { GestureViewer } from 'react-native-gesture-image-viewer';
function App() {
return ;
}
```
### `initialIndex` (default: `0`)
Sets the initial index value.
### `dismissThreshold` (default: `80`)
`dismissThreshold` controls when `onDismiss` is called by applying a threshold value during vertical gestures.
### `resistance` (default: `2`)
`resistance` controls the range of vertical movement by applying resistance during vertical gestures.
### `maxZoomScale` (default: `2`)
Controls the maximum zoom scale multiplier.
## `GestureViewerProps` interface
[Go to source](https://github.com/saseungmin/react-native-gesture-image-viewer/blob/edafaf0346c3f922382cd1e5f8c5c30d49115ec7/src/types.ts#L76)
````tsx title="GestureViewerProps"
export interface GestureViewerProps {
/**
* When you want to efficiently manage multiple `GestureViewer` instances, you can use the `id` prop to use multiple `GestureViewer` components.
* @remarks `GestureViewer` automatically removes instances from memory when components are unmounted, so no manual memory management is required.
* @defaultValue 'default'
*/
id?: string;
/**
* The data to display in the `GestureViewer`.
*/
data: ItemT[];
/**
* The index of the item to display in the `GestureViewer` when the component is mounted.
* @defaultValue 0
*/
initialIndex?: number;
/**
* A callback function that is called when the index of the item changes.
*/
onIndexChange?: (index: number) => void;
/**
* A callback function that is called when the `GestureViewer` is dismissed.
*/
onDismiss?: () => void;
/**
* A callback function that is called when the dismiss interaction starts.
* @remarks Useful to hide external UI (e.g., headers, buttons) while the dismiss gesture/animation is in progress.
*/
onDismissStart?: () => void;
/**
* A callback function that is called to render the item.
*/
renderItem: (item: ItemT, index: number) => React.ReactElement;
/**
* A callback function that is called to render the container.
* @remarks Useful for composing additional UI (e.g., close button, toolbars) around the viewer.
* The second argument provides control helpers such as `dismiss()` to close the viewer.
*
* @param children - The viewer content to be rendered inside your container.
* @param helpers - Control helpers for the viewer. Currently includes `dismiss()`.
* @returns A React element that wraps and renders the provided `children`.
*/
renderContainer?: (
children: React.ReactElement,
helpers: { dismiss: () => void },
) => React.ReactElement;
/**
* Support for any list component like `ScrollView`, `FlatList`, `FlashList` through the `ListComponent` prop.
*/
ListComponent: LC;
/**
* The width of the `GestureViewer`.
* @remarks If you don't set this prop, the width of the `GestureViewer` will be the same as the width of the screen.
* @defaultValue screen width
*/
width?: number;
/**
* The height of the `GestureViewer`.
* @remarks If you don't set this prop, the height of the `GestureViewer` will be the same as the height of the screen.
* @defaultValue screen height
*/
height?: number;
/**
* Auto play mode.
* @remarks
* - When `true`, the viewer will automatically play the next item after the specified interval.
* - When `enableLoop` is enabled, the viewer will loop back to the first item after the last item.
* - When `enableLoop` is disabled, the viewer will stop at the last item.
* - When there is only one item, auto-play is disabled.
* - When zoom or rotate gestures are detected, the auto-play will be paused.
* @defaultValue false
*/
autoPlay?: boolean;
/**
* Auto play interval.
* @remarks
* - When `autoPlay` is enabled, the viewer advances to the next item after the specified interval (ms).
* - Must be a positive integer. Values below 250ms are clamped to 250ms at runtime.
* @defaultValue 3000
*/
autoPlayInterval?: number;
/**
* Enables snap scrolling mode.
*
* @remarks
* **`false` (default)**: Paging mode (`pagingEnabled: true`)
* - Scrolls by full screen size increments
*
* **`true`**: Snap mode (`snapToInterval` auto-calculated)
* - `snapToInterval` is automatically calculated based on `width` and `itemSpacing` values
* - Use this option when you need item spacing
* @defaultValue false
*
*/
useSnap?: boolean;
/**
* `dismissThreshold` controls when `onDismiss` is called by applying a threshold value during vertical gestures.
* @defaultValue 80
*/
dismissThreshold?: number;
/**
* Calls `onDismiss` function when swiping down.
* @remarks Useful for closing modals with downward swipe gestures.
* @defaultValue true
*/
enableDismissGesture?: boolean;
/**
* Controls left/right swipe gestures.
* @remarks When `false`, horizontal gestures are disabled.
* @defaultValue true
*/
enableSwipeGesture?: boolean;
/**
* `resistance` controls the range of vertical movement by applying resistance during vertical gestures.
* @defaultValue 2
*/
resistance?: number;
/**
* The props to pass to the list component.
* @remarks The `listProps` provides **type inference based on the selected list component**, ensuring accurate autocompletion and type safety in your IDE.
*/
listProps?: Partial>;
/**
* The style of the backdrop.
*/
backdropStyle?: StyleProp;
/**
* The style of the container.
*/
containerStyle?: StyleProp;
/**
* By default, the background `opacity` gradually decreases from 1 to 0 during downward swipe gestures.
* @remarks When `false`, this animation is disabled.
* @defaultValue true
*/
animateBackdrop?: boolean;
/**
* Only works when zoom is active, allows moving item position when zoomed.
* @remarks When `false`, gesture movement is disabled during zoom.
* @defaultValue true
*/
enableZoomPanGesture?: boolean;
/**
* Controls two-finger pinch gestures.
* @remarks When `false`, two-finger zoom gestures are disabled.
* @defaultValue true
*/
enableZoomGesture?: boolean;
/**
* Controls double-tap zoom gestures.
* @remarks When `false`, double-tap zoom gestures are disabled.
* @defaultValue true
*/
enableDoubleTapGesture?: boolean;
/**
* Enables infinite loop navigation.
* @defaultValue false
*/
enableLoop?: boolean;
/**
* The maximum zoom scale.
* @defaultValue 2
*/
maxZoomScale?: number;
/**
* The spacing between items in pixels.
* @remarks Only applied when `useSnap` is `true`.
* @defaultValue 0
*/
itemSpacing?: number;
/**
* Trigger-based animation settings
* @remarks You can customize animation duration, easing, and system reduce-motion behavior.
*
* @example
* ```tsx
* {
* console.log('Animation complete');
* },
* }}
* />
* ```
*/
triggerAnimation?: TriggerAnimationConfig;
}
````
---
url: /1.x/guide/usage/handling-viewer-events.md
---
# Handling Viewer Events
You can subscribe to specific events from `GestureViewer` using the `useGestureViewerEvent` hook. This allows you to respond to real-time gesture changes like zoom and rotation.
```tsx
import { GestureViewer, useGestureViewerEvent } from 'react-native-gesture-image-viewer';
function App() {
// Listen to zoom changes on the default instance (ID: 'default')
useGestureViewerEvent('zoomChange', (data) => {
console.log(`Zoom changed from ${data.previousScale} to ${data.scale}`);
});
// Listen to rotation changes on the default instance (ID: 'default')
useGestureViewerEvent('rotationChange', (data) => {
console.log(`Rotation changed from ${data.previousRotation}Β° to ${data.rotation}Β°`);
});
// Listen to events on a specific instance
useGestureViewerEvent('gallery', 'zoomChange', (data) => {
console.log(`Gallery zoom: ${data.scale}x`);
});
return ;
}
```
#### Event Types
| Event | Description | Data |
| :--------------- | :------------------------------------------------------------- | :----------------------------------------------- |
| `zoomChange` | Fired when the zoom scale changes during pinch gestures | `{ scale: number, previousScale: number }` |
| `rotationChange` | Fired when the rotation angle changes during rotation gestures | `{ rotation: number, previousRotation: number }` |
---
url: /1.x/guide/usage/multi-instance-management.md
---
# Multi-Instance Management
When you want to efficiently manage multiple `GestureViewer` instances, you can use the `id` prop to use multiple `GestureViewer` components.
`GestureViewer` automatically removes instances from memory when components are unmounted, so no manual memory management is required.
:::note
The default `id` value is `default`.
:::
```tsx
import {
GestureViewer,
GestureTrigger,
useGestureViewerController,
useGestureViewerEvent,
} from 'react-native-gesture-image-viewer';
const firstViewerId = 'firstViewerId';
const secondViewerId = 'secondViewerId';
function App() {
const { currentIndex: firstCurrentIndex, totalCount: firstTotalCount } =
useGestureViewerController(firstViewerId);
const { currentIndex: secondCurrentIndex, totalCount: secondTotalCount } =
useGestureViewerController(secondViewerId);
useGestureViewerEvent(firstViewerId, 'zoomChange', (data) => {
console.log(`Gallery zoom: ${data.scale}x`);
});
useGestureViewerEvent(secondViewerId, 'zoomChange', (data) => {
console.log(`Gallery zoom: ${data.scale}x`);
});
return (
);
}
```
---
url: /1.x/guide/usage/programmatic-control.md
---
# Programmatic Control
You can programmatically control the `GestureViewer` using the `useGestureViewerController` hook.
```tsx
import { GestureViewer, useGestureViewerController } from 'react-native-gesture-image-viewer';
function App() {
const {
goToIndex,
goToPrevious,
goToNext,
currentIndex,
totalCount,
zoomIn,
zoomOut,
resetZoom,
rotate,
} = useGestureViewerController();
return (
{/* Zoom Controls & Rotation Controls */}
zoomIn(0.25)} />
zoomOut(0.25)} />
{
rotate(0);
resetZoom();
}}
/>
rotate(90)} />
rotate(90, false)} />
{/* Navigation Controls */}
goToIndex(2)} />
{`${currentIndex + 1} / ${totalCount}`}
);
}
```
## `useGestureViewerController` API Reference
| Property | Description | Type | Default |
| :------------- | :----------------------------------------- | :----------------------------------------------------- | :--------: |
| `goToIndex` | Navigate to a specific index. | `(index: number) => void` | - |
| `goToPrevious` | Navigate to the previous item. | `() => void` | - |
| `goToNext` | Navigate to the next item. | `() => void` | - |
| `currentIndex` | The index of the currently displayed item. | `number` | `0` |
| `totalCount` | The total number of items. | `number` | `0` |
| `zoomIn` | Zoom in by the specified multiplier. | `(multiplier?: number) => void` | `0.25` |
| `zoomOut` | Zoom out by the specified multiplier. | `(multiplier?: number) => void` | `0.25` |
| `resetZoom` | Reset zoom to the specified scale. | `(scale?: number) => void` | `1` |
| `rotate` | Rotate by the specified angle. | `(angle?: RotationAngle, clockwise?: boolean) => void` | `90, true` |
### Parameters
- `zoomIn(multiplier?)`
- **multiplier**: The multiplier for zooming in (range: `0.01 ~ 1`)
- Example: `zoomIn(0.5)` β Zoom in by an additional 50% of the current scale
- `zoomOut(multiplier?)`
- **multiplier**: The multiplier for zooming out (range: `0.01 ~ 1`)
- Example: `zoomOut(0.3)` β Zoom out by dividing the current scale by 1.3
- `resetZoom(scale?)`
- **scale**: The scale value to reset to
- Example: `resetZoom(1.5)` β Reset to 1.5x scale
- `rotate(angle?, clockwise?)`
- **angle**: The angle to rotate (0, 90, 180, 270, 360)
- **clockwise**: The direction to rotate (true: clockwise, false: counter-clockwise)
- Example: `rotate(90)` β Rotate 90 degrees clockwise
- Example: `rotate(90, false)` β Rotate 90 degrees counter-clockwise
- Example: `rotate(0)` β Reset rotation
---
url: /1.x/guide/usage/style-customization.md
---
# Style Customization
You can customize the styling of `GestureViewer`.
```tsx
import { GestureViewer } from 'react-native-gesture-image-viewer';
function App() {
return (
{children}}
/>
);
}
```
| Property | Description | Default |
| :----------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------: |
| `animateBackdrop` | By default, the background `opacity` gradually decreases from 1 to 0 during downward swipe gestures. When `false`, this animation is disabled. | `true` |
| `width` | The width of content items. Default is window width. | `Dimensions width` |
| `height` | The height of content items. Default is window height. | `Dimensions height` |
| `containerStyle` | Allows custom styling of the container that wraps the list component. | `flex: 1` |
| `backdropStyle` | Allows customization of the viewer's background style. | `backgroundColor: black; StyleSheet.absoluteFill;` |
| `renderContainer(children, helpers)` | Allows custom wrapper component around ``. | |
---
url: /1.x/guide/usage/trigger-based-animations.md
---
# Trigger-Based Modal Animations
`GestureTrigger` supports smooth trigger-based animations that create seamless transitions from trigger elements (like thumbnails) to the full modal view. This feature enhances user experience by maintaining visual continuity between the trigger and modal content.
## GestureTrigger Usage
The `GestureTrigger` component wraps pressable elements and registers their position for smooth modal transitions.
```tsx
import { GestureTrigger, GestureViewer } from 'react-native-gesture-image-viewer';
function Gallery() {
const [visible, setVisible] = useState(false);
const [selectedIndex, setSelectedIndex] = useState(0);
const openModal = (index: number) => {
setSelectedIndex(index);
setVisible(true);
};
return (
{/* Gallery Grid */}
{images.map((uri, index) => (
openModal(index)}>
))}
{/* Modal */}
setVisible(false)}
triggerAnimation={{
duration: 300,
easing: Easing.bezier(0.25, 0.1, 0.25, 1.0),
onAnimationComplete: () => console.log('Animation finished!'),
}}
/>
);
}
```
### 1. Using onPress on GestureTrigger (Recommended)
```tsx
openModal(index)}>
```
### 2. Using onPress on Child Element
```tsx
openModal(index)}>
```
:::note important
- Both methods are functionally equivalent
- The `GestureTrigger` will automatically inject the press handler into the child component
- The child component must be pressable (support onPress prop)
- If both `GestureTrigger` and child have onPress, both will be called (child's handler first)
**Supported Child Components**
You can use any pressable component as a child, such as:
- `Pressable`
- `TouchableOpacity`
- `TouchableHighlight`
- `Button`
- Any custom component that accepts `onPress` prop
**Example with Different Components**
```tsx
// Using TouchableOpacity
View Image
;
// Using custom component
function CustomButton({ onPress, children }) {
return {children};
}
Custom Button
;
```
:::
## Animation Configuration
You can customize the trigger animation behavior using the `triggerAnimation` prop:
```tsx
import { ReduceMotion, Easing } from 'react-native-reanimated';
function App() {
return (
{
// Callback when animation finishes
console.log('Modal animation completed');
},
}}
/>
);
}
```
## Multiple Trigger Instances
You can have multiple trigger instances by using different IDs:
```tsx
// Photo gallery triggers
openPhotoModal(index)}>
// Video gallery triggers
openVideoModal(index)}>
```
:::tip
Make sure the `id` prop matches between `GestureTrigger` and `GestureViewer` components for the animation to work properly. (default value: `default`)
:::
## Handling Dismissal Animations
### onDismissStart Callback
The `onDismissStart` callback is triggered immediately when the dismiss animation begins, which is useful for hiding UI elements that should disappear before the animation completes.
```tsx
function App() {
const [visible, setVisible] = useState(false);
const [showExternalUI, setShowExternalUI] = useState(false); // [!code highlight]
// [!code highlight:3]
const handleDismissStart = () => {
setShowExternalUI(false);
};
return (
// [!code highlight:5]
{showExternalUI && (
{`${currentIndex + 1} / ${totalCount}`}
)}
);
}
```
### Dismissing from Custom Components
You can dismiss the viewer programmatically using the `dismiss` helper from `renderContainer`:
```tsx
( // [!code highlight]
{children}
Close
)}
/>
```
:::note Why Use renderContainer's dismiss?
When using trigger-based animations, it's important to use the `dismiss` method provided by `renderContainer` instead of directly controlling the visibility with `setVisible(false)`. Here's why:
```tsx
// β Avoid: This will close immediately without trigger animation
setVisible(false)} title="Close" />
// β
Preferred: This will use the trigger animation to close
```
**How It Works:**
1. With Trigger Animation:
- When `dismiss` is called, the viewer will animate back to the original trigger position
- `onDismissStart` is called at the start of the animation
- `onDismiss` is called after the animation completes
2. Without Trigger Animation:
- If no trigger animation is configured, `dismiss` will still work as a simple close
:::
### Complete Example with Dismiss Handling
```tsx
function ImageViewer() {
const [visible, setVisible] = useState(false);
const [showUI, setShowUI] = useState(true);
return (
setShowUI(false)}
onDismiss={() => setVisible(false)}
renderContainer={(children, { dismiss }) => (
{children}
{showUI && (
)}
)}
/>
);
}
```
### Dismiss Behavior with Trigger Animation
When using trigger-based animations, the dismiss animation will animate back to the original trigger position. The `onDismissStart` callback is called at the start of this animation, allowing you to hide any UI elements that should not be visible during the dismiss animation.
```tsx
{
console.log('Dismiss animation started');
setShowUI(false);
}}
onDismiss={() => {
console.log('Dismiss animation complete');
setVisible(false);
}}
/>
```
This pattern ensures a smooth user experience by:
1. Immediately hiding UI elements when dismiss starts
2. Allowing the dismiss animation to complete naturally
3. Cleaning up any resources only after the animation is fully complete
### Best Practices
1. Always use the `dismiss` method from `renderContainer` when you want to close the viewer with animations
2. Use `onDismissStart` to hide UI elements that shouldn't be visible during the dismiss animation
3. Use `onDismiss` for cleanup operations that should happen after the animation completes
#### Common Pitfalls
```tsx
// β Avoid: This will bypass the trigger animation
setVisible(false)} title="Close" />
// β Avoid: This will cause the animation to break
const handleClose = () => {
setShowUI(false);
setVisible(false); // Closes too early
};
// β
Correct: Let the animation complete naturally
setShowUI(false)}
onDismiss={() => setVisible(false)}
renderContainer={(children, { dismiss }) => (
{children}
{showUI && (
)}
)}
/>
```
This pattern ensures that your trigger-based animations work consistently and provides the best user experience.
## API Reference
### GestureTrigger Props
```ts
interface GestureTriggerProps {
id?: string; // Identifier to associate with GestureViewer (default: "default")
children: ReactElement; // Single pressable child element
onPress?: (...args: unknown[]) => void; // Additional onPress handler
}
```
### TriggerAnimation Config
```ts
interface TriggerAnimationConfig {
duration?: number; // Animation duration in milliseconds
easing?: EasingFunction; // Custom easing function
reduceMotion?: boolean; // Respect system reduce motion preference
onAnimationComplete?: () => void; // Callback fired when animation completes
}
```
| Property | Type | Default | Description |
| :-------------------- | :--------------- | :------------------------------------ | :----------------------------------------------- |
| `duration` | `number` | `300` | Animation duration in milliseconds |
| `easing` | `EasingFunction` | `Easing.bezier(0.25, 0.1, 0.25, 1.0)` | Easing function for the animation |
| `reduceMotion` | `ReduceMotion` | `undefined` | Whether to respect system reduce motion settings |
| `onAnimationComplete` | `() => void` | `undefined` | Callback fired when the animation completes |
:::note
The trigger animation works by measuring the trigger element's position when pressed and animating the modal from that position to full screen.
:::
---
url: /1.x/index.md
---
# React Native Gesture Image Viewer
Smooth and flexible viewer
> Reanimated-powered image gestures with full control
[Quick Start](/1.x/guide/getting-started/installation.html) | [GitHub](https://github.com/saseungmin/react-native-gesture-image-viewer)
## Features
- π€ **Complete Gesture Support**: Pinch zoom, double-tap zoom, swipe navigation, pan when zoomed-in, and vertical drag to dismiss
- ποΈ **High-Performance Animations**: Smooth and responsive animations at 60fps and beyond, powered by React Native Reanimated
- π¨ **Full Customization**: Total control over components, styles, and gesture behavior
- ποΈ **External Control API**: Trigger actions programmatically from buttons or other UI components
- π§© **Multi-Instance Management**: Manage multiple viewers independently using unique IDs
- 𧬠**Flexible Integration**: Works seamlessly with Modal, FlatList, FlashList, Expo Image, FastImage, and more
- π§ **Full TypeScript Support**: Great developer experience with type inference and safety
- π **Cross-Platform Support**: Runs on iOS, Android, and Web with Expo Go and New Architecture compatibility
- πͺ **Easy-to-Use API**: Simple and intuitive API that requires minimal setup