Description:
react-native-zoom-grid is a React Native component that creates resizable & zoomable grid layouts with interactive pinch-to-zoom functionality.
The component utilizes @shopify/flash-list for list virtualization and react-native-reanimated to handle smooth layout transitions on the UI thread.
It replicates the user experience found in the native iOS Photos app, where users pinch to adjust the number of columns dynamically.
Features
- 📸 Zoom Transitions: Changes column counts smoothly through gesture-driven animations.
- ⚡ Performance Optimization: Executes animations on the UI thread using
react-native-reanimatedto maintain 60fps. - 🖐 Gesture Control: Tracks precise pinch interactions via
react-native-gesture-handler. - 🎨 Column Customization: Accepts an array of numbers to define valid column counts for each zoom level.
- 🔄 Infinite Loading: Triggers
onEndReachedto fetch more data as the user scrolls. - ↔️ Inverted Mode: Supports inverted scrolling for chat interfaces or reverse timelines.
See It In Action
Use Cases
- Photo Galleries: Build an image grid where users can zoom to see more or fewer photos per row.
- Product Listings: Display items in an e-commerce catalog with adjustable view density.
- Dashboard Widgets: Arrange data tiles or cards that users can zoom to reorganize.
- Media Libraries: Create a view for videos or audio files with multiple layout options.
How to Use It
1. Install the react-native-zoom-grid package and its dependencies.
npm install react-native-zoom-grid @shopify/flash-list react-native-reanimated react-native-gesture-handler react-native-safe-area-context2. Follow the setup guides for react-native-reanimated and react-native-gesture-handler. You must wrap your app in a GestureHandlerRootView from the gesture handler library.
3. Import the ZoomGrid component into your screen. Pass your data array and a function to render each item. The zoomLevels prop controls how many columns appear at each pinch step.
import React from 'react';
import { View, Text, Image } from 'react-native';
import { ZoomGrid } from 'react-native-zoom-grid';
const App = () => {
const [data, setData] = React.useState(myDataArray);
const renderItem = ({ item, size }) => {
return (
<View style={{ width: size, height: size, backgroundColor: '#eee' }}>
<Image source={{ uri: item.url }} style={{ flex: 1 }} />
<Text>{item.title}</Text>
</View>
);
};
return (
<ZoomGrid
data={data}
renderItem={renderItem}
zoomLevels={[6, 4, 2]} // Columns at each zoom level
initialNumColumns={4}
keyExtractor={(item) => item.id}
onEndReached={() => { /* Fetch more data */ }}
/>
);
};4. The component exposes an onZoomChange callback. You can use this to track the current layout state or persist the user’s preference.
<ZoomGrid
data={photos}
// ... other props
onZoomChange={(columns) => {
console.log(`Current layout: ${columns} columns`);
}}
/>5. All available props for the <ZoomGrid /> component.
| Prop | Type | Default | Description |
|---|---|---|---|
data | Array<T> | Required | The source array of items to render. |
renderItem | Function | Required | Renders individual grid cells. Receives item data and dynamic size. |
zoomLevels | number[] | [5, 3, 1] | Defines the allowed column counts (e.g., max zoom out to max zoom in). |
initialNumColumns | number | 3 | Sets the starting column count. |
onZoomChange | (cols: number) => void | – | Executes when the user completes a zoom gesture. |
renderHeader | Function | – | Renders a component at the top of the scroll view. |
contentInsets | Object | – | Applies padding to the content container ({ top, bottom }). |
invert | boolean | true | Reverses the scroll direction and item order. |
gridStyle | ViewStyle | – | Applies custom styles to the grid container. |
Related Resources
- React Native Reanimated: Handles the low-level animation primitives used by the grid.
- FlashList: Manages the recycling and rendering of large lists with high performance.
- React Native Gesture Handler: Provides the native-driven gesture management system.
FAQs
Q: Does this work with Expo?
A: Yes, it works with Expo as long as you install the required native dependencies and configure the babel plugin for Reanimated.
Q: Can I use different aspect ratios for items?
A: The size prop provided to renderItem assumes a square layout by default. You can apply custom styles, but the grid logic calculates layout based on column division.
Q: How does it handle large datasets?
A: It uses @shopify/flash-list internally. This system recycles views to maintain low memory usage even with thousands of items.
Q: Is the zoom continuous or stepped?
A: The gesture feels continuous during the pinch, but the final layout snaps to one of the defined zoomLevels when you release.





