Cell Separator in VirtualizedList
Published On: 2024-07-16
Posted By: Harish

Sometimes we may need to add a different view in a list. We cannot use this view in the list as this will behave as a list item. Let's say we want to add a line in between two list items..
We can use contentContainerStyles prop to add margin or padding to the content cells but not a line. For that we have to add a bottom line to the cell view. But this will make us write extra code to remove that line for the bottom item. This can become more work when another view component is used other than a line.
So, to avoid that hectic work, we have a callback called ItemSeparatorComponent which will render a component in between two list items ignoring top and bottom places of the list.
Let's check the working of this separator in action.
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 colorful vertical VirtualizedList and add a black line in between color blocks.
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} />
)
}}
/>
...
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, we need to add a black separator in between the items.
So, create a new component for the black line, add ItemSeparatorComponent
to the VirtualizedList and use that newly created component to return in this callback.
...
const LineSeparator = () => {
return (
<View style={styles.separator} />
)
};
...
<VirtualizedList
...
ItemSeparatorComponent={() => {
return <LineSeparator />
}}
/>
...
If you re-run the app, you will see a black line in between color blocks.

Using this ItemSeparatorComponent
function, we can implement conditional rendering of this separator. This function receives props related to the latest rendered item.
Add props to the separator component and console log it. You will see logs similar to below logs.
LOG {"highlighted": false, "leadingItem": {"color": "orange", "id": "0.892a7a57495773b"}}
LOG {"highlighted": false, "leadingItem": {"color": "green", "id": "0.917769b1694ba05"}}
...
Lets say, we want a black line in between darkorange
and gold
blocks.
For that, get the leadingItem's color and write a condition to apply line only when leading item is darkorange
...
type ItemSeparatorProp = {
highlighted: boolean,
leadingItem: ItemDataProp
}
...
const LineSeparator = ({ itemData }: { itemData: ItemSeparatorProp }) => {
const { color } = itemData.leadingItem;
return (
color === 'darkorange'
? <View style={styles.separator} />
: null
)
};
...
<VirtualizedList
...
ItemSeparatorComponent={(props) => {
return <LineSeparator itemData={props} />
}}
/>
...
If you rebuild the metro, you will see a black line in between dark orange and gold blocks only.

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;
};
type ItemSeparatorProp = {
highlighted: boolean,
leadingItem: ItemDataProp
}
export default function App() {
const Item = (props: ItemProp) => {
const { color } = props;
return (
<View
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: string[]) => data.length;
const LineSeparator = ({ itemData }: { itemData: ItemSeparatorProp }) => {
const { color } = itemData.leadingItem;
return (
color === 'darkorange'
? <View style={styles.separator} />
: null
)
};
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}>
row separator in VirtualizedList
</Text>
<VirtualizedList
contentContainerStyle={styles.content_container}
data={colors}
getItemCount={getItemCount}
getItem={getItem}
keyExtractor={item => item?.id}
ItemSeparatorComponent={(props) => {
return <LineSeparator itemData={props} />
}}
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'
},
separator: {
marginTop: 10,
height: 4,
backgroundColor: 'black',
borderRadius: 10
}
});