
Product
Announcing Precomputed Reachability Analysis in Socket
Socket’s precomputed reachability slashes false positives by flagging up to 80% of vulnerabilities as irrelevant, with no setup and instant results.
react-native-gesture-image-viewer
Advanced tools
🖼️ A highly customizable and easy-to-use React Native image viewer with gesture support and external controls
English | 한국어
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 and Gesture Handler, providing complete customization and intuitive gesture support for not only images but also videos, custom components, and any other content.
[!IMPORTANT]
react-native-gesture-image-viewer
is a high-performance viewer library built onreact-native-reanimated
andreact-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.
npm install react-native-reanimated
npm install react-native-gesture-handler
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 |
Add the plugin to your babel.config.js
:
// babel.config.js
module.exports = {
presets: [
... // don't add it here :)
],
plugins: [
...
// for web
'@babel/plugin-proposal-export-namespace-from',
// react-native-reanimated/plugin has to be listed last.
'react-native-reanimated/plugin',
],
};
Wrap your Metro config with wrapWithReanimatedMetroConfig
in metro.config.js
:
const {
wrapWithReanimatedMetroConfig,
} = require('react-native-reanimated/metro-config');
const config = {
// Your existing Metro configuration options
};
module.exports = wrapWithReanimatedMetroConfig(config);
react-native-gesture-handler
generally doesn't require additional setup, but please refer to the official documentation for your specific environment.GestureHandlerRootView
. However, this library already includes GestureHandlerRootView
internally, so no additional wrapping is needed when using modals.# npm
npm install react-native-gesture-image-viewer
# pnpm
pnpm add react-native-gesture-image-viewer
# yarn
yarn add react-native-gesture-image-viewer
# bun
bun add react-native-gesture-image-viewer
react-native-gesture-image-viewer
is a library focused purely on gesture interactions for complete customization.
You can create a viewer using any Modal
of your choice as shown below:
import { FlatList, Image, Modal } from 'react-native';
import { GestureViewer } from 'react-native-gesture-image-viewer';
function App() {
const images = [...];
const [visible, setVisible] = useState(false);
// Wrap with useCallback for performance optimization
const renderImage = useCallback((imageUrl: string) => {
return <Image source={{ uri: imageUrl }} style={{ width: '100%', height: '100%' }} resizeMode="contain" />;
}, []);
return (
<Modal visible={visible} onRequestClose={() => setVisible(false)}>
<GestureViewer
data={images}
renderItem={renderImage}
ListComponent={FlatList}
onDismiss={() => setVisible(false)}
/>
</Modal>
);
}
react-native-gesture-image-viewer
supports various gestures essential for viewers. All gestures are enabled by default.
function App() {
return (
<GestureViewer
data={images}
renderItem={renderImage}
enableLoop={false}
enableDismissGesture
enableSwipeGesture
enableZoomGesture
enableDoubleTapGesture
enableZoomPanGesture
/>
)
}
Property | Description | Default |
---|---|---|
enableLoop | 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. | false |
enableDismissGesture | Calls onDismiss function when swiping down. Useful for closing modals with downward swipe gestures. | true |
enableSwipeGesture | Controls left/right swipe gestures. When false , horizontal gestures are disabled. | true |
enableZoomGesture | 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. | true |
enableDoubleTapGesture | Controls double-tap zoom gestures with precision targeting. When false , double-tap zoom is disabled. Zoom centers exactly on the tapped location. | true |
enableZoomPanGesture | Enables panning when zoomed in with automatic boundary detection. When false , movement is disabled during zoom. Prevents content from moving outside visible screen area. | true |
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.
Support for any list component like ScrollView
, FlatList
, FlashList
through the ListComponent
prop.
The listProps
provides type inference based on the selected list component, ensuring accurate autocompletion and type safety in your IDE.
import { FlashList } from '@shopify/flash-list';
function App() {
return (
<GestureViewer
data={images}
ListComponent={FlashList}
listProps={{
// ✅ FlashList props autocompletion
}}
/>
);
}
You can inject various types of content components like expo-image
, FastImage
, etc., through the renderItem
prop to use gestures.
import { GestureViewer } from 'react-native-gesture-image-viewer';
import { Image } from 'expo-image';
function App() {
const renderImage = useCallback((imageUrl: string) => {
return <Image source={{ uri: imageUrl }} style={{ width: '100%', height: '100%' }} contentFit="contain" />;
}, []);
return (
<GestureViewer
data={images}
renderItem={renderImage}
/>
);
}
You can programmatically control the GestureViewer
using the useGestureViewerController
hook.
import { GestureViewer, useGestureViewerController } from 'react-native-gesture-image-viewer';
function App() {
const {
goToIndex,
goToPrevious,
goToNext,
currentIndex,
totalCount,
zoomIn,
zoomOut,
resetZoom,
rotate
} = useGestureViewerController();
return (
<View>
<GestureViewer
data={images}
renderItem={renderImage}
/>
{/* Zoom Controls */}
<View>
<Feather.Button name="zoom-in" onPress={() => zoomIn(0.25)} />
<Feather.Button name="zoom-out" onPress={() => zoomOut(0.25)} />
<Feather.Button
name="refresh-cw"
onPress={() => {
rotate(0);
resetZoom();
}}
/>
<Feather.Button name="rotate-cw" onPress={() => rotate(90)} />
<Feather.Button name="rotate-ccw" onPress={() => rotate(90, false)} />
</View>
{/* Navigation Controls */}
<View>
<Feather.Button name="chevron-left" onPress={goToPrevious} />
<Button title="Jump to index 2" onPress={() => goToIndex(2)} />
<Feather.Button name="chevron-right" onPress={goToNext} />
<Text>{`${currentIndex + 1} / ${totalCount}`}</Text>
</View>
</View>
);
}
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?: number, clockwise?: boolean) => void | 90, true |
zoomIn(multiplier?)
0.01 ~ 1
)zoomIn(0.5)
→ Zoom in by an additional 50% of the current scalezoomOut(multiplier?)
0.01 ~ 1
)zoomOut(0.3)
→ Zoom out by dividing the current scale by 1.3resetZoom(scale?)
resetZoom(1.5)
→ Reset to 1.5x scalerotate(angle?, clockwise?)
rotate(90)
→ Rotate 90 degrees clockwiserotate(90, false)
→ Rotate 90 degrees counter-clockwiserotate(0)
→ Reset rotationYou 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.
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 (
<GestureViewer
data={images}
renderItem={renderImage}
/>
);
}
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 } |
You can customize the styling of GestureViewer
.
import { GestureViewer } from 'react-native-gesture-image-viewer';
function App() {
return (
<GestureViewer
animateBackdrop={false}
width={400}
containerStyle={{ /* ... */ }}
backdropStyle={{ backgroundColor: 'rgba(0, 0, 0, 0.90)' }}
renderContainer={(children) => <View style={{ flex: 1 }}>{children}</View>}
/>
);
}
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 |
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 | Allows custom wrapper component around <GestureViewer /> . |
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.
The default
id
value isdefault
.
import { GestureViewer, useGestureViewerController } 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);
return (
<View>
<GestureViewer
id={firstViewerId}
data={images}
renderItem={renderImage}
/>
<GestureViewer
id={secondViewerId}
data={images}
renderItem={renderImage}
/>
</View>
);
}
onIndexChange
The onIndexChange
callback function is called when the index
value changes.
import { GestureViewer } from 'react-native-gesture-image-viewer';
function App() {
const [currentIndex, setCurrentIndex] = useState(0);
return (
<GestureViewer
onIndexChange={setCurrentIndex}
/>
);
}
useSnap
Sets the scroll behavior mode. false
(default) uses paging mode, true
uses snap mode.
import { GestureViewer } from 'react-native-gesture-image-viewer';
function App() {
return (
<GestureViewer
data={data}
renderItem={renderItem}
useSnap={true}
/>
);
}
itemSpacing
Sets the spacing between items in pixels. Only applied when useSnap
is true
.
import { GestureViewer } from 'react-native-gesture-image-viewer';
function App() {
return (
<GestureViewer
data={data}
renderItem={renderItem}
useSnap={true}
itemSpacing={16} // 16px spacing between items
/>
);
}
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.
renderItem
function with useCallback
to prevent unnecessary re-renders.FastImage
or expo-image
.FlashList
.For details on how to contribute to the project and set up the development environment, please refer to the Contributing Guide.
1.6.1
9e5a6bd: refactor(loop): replace timeout with event-driven loop animation
FAQs
🖼️ A highly customizable and easy-to-use React Native image viewer with gesture support and external controls
We found that react-native-gesture-image-viewer demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 0 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Product
Socket’s precomputed reachability slashes false positives by flagging up to 80% of vulnerabilities as irrelevant, with no setup and instant results.
Product
Socket is launching experimental protection for Chrome extensions, scanning for malware and risky permissions to prevent silent supply chain attacks.
Product
Add secure dependency scanning to Claude Desktop with Socket MCP, a one-click extension that keeps your coding conversations safe from malicious packages.