Cell Metrics in VirtualizedList
Published On: 2024-07-15
Posted By: Harish

We have seen renderItem callback in react native VirtualizedList where we return a cell component to render the list cell. In a few scenarios, we may need to get events of the item cell like getting the position or getting its size etc.
In those situations, we can use CellRenderedComponent callback to get the details of the cell rather than using renderItem component.
We can't use renderItem
because this callback's cell returns events relative to its single item view whereas CellRendererComponent returns cell events relative to the list view. You can take a look at VirtualizedList github code file for more info.
In short, use renderItem callback to render a cell and use CellRendererComponent to get the metrics of the rendered cell/row.
So, let's get the metrics of the rendered cell in VirtualizedList with an example.
Create A New Project
Create a new react-native project by using npx. Check documentation for creating a new react native project.
npx react-native@latest init VListRN
Example Implementation
We will create a simple vertical list with color blocks and get the position of the cell in the VirtualizedList.
Import and add VirtualizedList
. Add basic VirtualizedList with color blocks. Check basic VirtualizedList implementation for more info.
//App.tsx
...
import { VirtualizedList } from 'react-native';
...
<VirtualizedList
contentContainerStyle={styles.content_container}
data={colors}
getItemCount={getItemCount}
getItem={getItem}
keyExtractor={item => item.id}
renderItem={({ item }) => {
return (
<Item color={item.color} />
)
}}
/>
...
Check the complete code block below for above functions.
Now run the app,
#for Android
npx react-native run-android
#for ios
npx react-native run-ios
You will see a few colored blocks.

Now for better understanding, let's get the position using renderItem
callback.
So, add the onLayout event to the renderItem view and console log the output.
...
const Item = (props: ItemProp) => {
...
return (
<View
onLayout={e => console.log(e.nativeEvent)}
...
/>
)
};
...
If you re-run the app, you will see a log of the onLayout event’s nativeEvent object with every cell position to x-axis to 0
and y-axis to 0
(0, 0).
LOG {"layout": {"height": 200, "width": 355, "x": 0, "y": 0}, "target": 415}
LOG {"layout": {"height": 200, "width": 355, "x": 0, "y": 0}, "target": 419}
LOG {"layout": {"height": 200, "width": 355, "x": 0, "y": 0}, "target": 425}
...
Now, remove onLayout from renderItem component and add CellRendererComponent
callback.
CellRendererComponent returns item info relative to the list view, we have to wrap the item in a View to get its position. So, we use another View in CellRendererComponent to get the row's position.
...
<VirtualizedList
...
CellRendererComponent={(props) => {
const { children } = props;
return (
<View onLayout={e => console.log(e.nativeEvent)}>
{children}
</View>
)
}}
/>
...
Rebuild the metro builder to get the position logs.
It gets few props and children
is one of them. You can find other props in CellRendererComponent props list
LOG {"layout": {"height": 200, "width": 355, "x": 0, "y": 0}, "target": 269}
LOG {"layout": {"height": 200, "width": 355, "x": 0, "y": 210}, "target": 275}
LOG {"layout": {"height": 200, "width": 355, "x": 0, "y": 420}, "target": 279}
LOG {"layout": {"height": 200, "width": 355, "x": 0, "y": 630}, "target": 285}
...
In this way, we can get the metrics of the rendered row in VirtualizedList.
Complete code of our example,
//App.tsx
import React from "react";
import {
Text,
StyleSheet,
SafeAreaView,
StatusBar,
View,
VirtualizedList,
} from "react-native";
const colors = [
'orange',
'green',
'blue',
'maroon',
'violet',
'darkorange',
'gold',
'darkgreen',
'aquamarine',
'cadetblue'
];
type ItemProp = {
color: string;
};
type ItemDataProp = {
id: string;
color: string;
};
export default function App() {
const Item = (props: ItemProp) => {
const { color } = props;
return (
<View
//onLayout={e => console.log(e.nativeEvent)}
key={color}
style={[
styles.view,
{
backgroundColor: color
}
]}
/>
)
};
const getItem = (data: string[], index: number) => ({
id: Math.random().toString(12).substring(0),
color: data[index],
} as ItemDataProp);
const getItemCount = (data: any) => data.length;
return (
<SafeAreaView style={{ flex: 1, backgroundColor: 'white' }}>
<StatusBar
barStyle="dark-content"
/>
<View style={styles.container}>
<Text style={styles.text}>
ifelsething.com
</Text>
<Text style={styles.text}>
metrics of the VirtualizedList cell/row
</Text>
<VirtualizedList
contentContainerStyle={styles.content_container}
data={colors}
getItemCount={getItemCount}
getItem={getItem}
keyExtractor={item => item.id}
CellRendererComponent={(props) => {
const { children } = props;
return (
<View onLayout={e => console.log(e.nativeEvent)}>
{children}
</View>
)
}}
renderItem={({ item }) => {
return (
<Item color={item.color} />
)
}}
/>
</View>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
margin: 10,
gap: 20
},
text: {
fontSize: 15,
color: 'black',
fontStyle: 'italic'
},
content_container: {
gap: 10
},
view: {
flexShrink: 1,
width: '100%',
height: 200,
borderRadius: 10,
alignSelf: 'center'
}
});