How to Create a React Native Grid Using FlatList: Complete Implementation Guide

Use the FlatList component with the numColumns prop to create a react native grid layout that efficiently renders scrollable columns.

React Native provides the FlatList component as the modern replacement for the legacy ListView, offering built-in virtualization and performance optimizations. When you need to display data in a react native grid format rather than a single column, FlatList eliminates the need for external libraries by exposing a simple numColumns prop that transforms the vertical list into a multi-column layout.

How FlatList Implements Grid Layout in React Native

Under the hood, FlatList is built on top of VirtualizedList, which efficiently recycles rows as they scroll off-screen. When you specify the numColumns prop, each rendered item gets wrapped in a row container that contains exactly numColumns children. This layout logic stays within the core library, meaning you only need to provide a function that renders a single cell.

The virtualization engine maintains a windowSize that determines how many items to render outside the visible viewport. For grid layouts, this is particularly important because each "row" in the virtualized view actually contains multiple data items.

React Native Grid Implementation: Code Examples

Basic Two-Column Grid

This example demonstrates the minimal configuration required to render a react native grid with two columns:

import React from 'react';
import {FlatList, View, Text, StyleSheet, Dimensions} from 'react-native';

const DATA = Array.from({length: 30}, (_, i) => ({
  id: `${i}`,
  title: `Item ${i + 1}`,
}));

export default function SimpleGrid() {
  const numColumns = 2;
  const itemWidth = Dimensions.get('window').width / numColumns;

  const renderItem = ({item}) => (
    <View style={[styles.item, {width: itemWidth}]}>
      <Text style={styles.title}>{item.title}</Text>
    </View>
  );

  return (
    <FlatList
      data={DATA}
      renderItem={renderItem}
      keyExtractor={item => item.id}
      numColumns={numColumns}
      showsVerticalScrollIndicator={false}
    />
  );
}

const styles = StyleSheet.create({
  item: {
    backgroundColor: '#f9c2ff',
    margin: 4,
    alignItems: 'center',
    justifyContent: 'center',
    height: 100,
  },
  title: {
    fontSize: 16,
  },
});

Key implementation details:

  • numColumns={2} instructs FlatList to arrange two items per row.
  • itemWidth is calculated so each cell fills exactly half the screen width.
  • keyExtractor returns a stable string ID to maintain component identity during recycling.

Grid with Spacing and Custom Separators

For production applications, you typically need controlled spacing between cells. This example uses columnWrapperStyle and ItemSeparatorComponent:

import React from 'react';
import {
  FlatList,
  View,
  Text,
  StyleSheet,
  TouchableOpacity,
} from 'react-native';

const photos = [
  /* an array of objects {id, uri, title} */
];

export default function PhotoGrid() {
  const columns = 3;

  const renderItem = ({item}) => (
    <TouchableOpacity style={styles.cell}>
      <View style={styles.imagePlaceholder} />
      <Text style={styles.caption}>{item.title}</Text>
    </TouchableOpacity>
  );

  const ItemSeparator = () => <View style={styles.vertSep} />;

  return (
    <FlatList
      data={photos}
      renderItem={renderItem}
      keyExtractor={item => item.id}
      numColumns={columns}
      columnWrapperStyle={styles.row}
      ItemSeparatorComponent={ItemSeparator}
      contentContainerStyle={styles.container}
    />
  );
}

const styles = StyleSheet.create({
  container: {
    padding: 8,
  },
  row: {
    justifyContent: 'space-between',
  },
  cell: {
    flex: 1,
    margin: 4,
    backgroundColor: '#eaeaea',
    alignItems: 'center',
    padding: 8,
  },
  vertSep: {
    width: 8,
  },
  imagePlaceholder: {
    width: '100%',
    height: 80,
    backgroundColor: '#bbb',
  },
  caption: {
    marginTop: 4,
    fontSize: 12,
  },
});

Critical styling concepts:

  • columnWrapperStyle applies a style to the row container that holds the numColumns items.
  • ItemSeparatorComponent inserts a vertical spacer between columns.
  • Using flex: 1 on each cell ensures equal width distribution regardless of device dimensions.

Responsive Grid Handling Orientation Changes

For a truly adaptive react native grid, listen to dimension changes and recalculate columns dynamically:

import React, {useState, useEffect} from 'react';
import {
  FlatList,
  View,
  Text,
  StyleSheet,
  Dimensions,
} from 'react-native';

export default function ResponsiveGrid() {
  const [numColumns, setNumColumns] = useState(2);
  const [itemSize, setItemSize] = useState(0);

  const updateLayout = () => {
    const {width, height} = Dimensions.get('window');
    // Switch to 3 columns in landscape, 2 in portrait
    const cols = width > height ? 3 : 2;
    setNumColumns(cols);
    setItemSize(width / cols);
  };

  useEffect(() => {
    updateLayout(); // initial
    const sub = Dimensions.addEventListener('change', updateLayout);
    return () => sub?.remove();
  }, []);

  const renderItem = ({item}) => (
    <View style={[styles.item, {width: itemSize, height: itemSize}]}>
      <Text>{item.title}</Text>
    </View>
  );

  return (
    <FlatList
      data={Array.from({length: 20}, (_, i) => ({
        id: `${i}`,
        title: `#${i + 1}`,
      }))}
      renderItem={renderItem}
      keyExtractor={item => item.id}
      numColumns={numColumns}
    />
  );
}

const styles = StyleSheet.create({
  item: {
    backgroundColor: '#b3e5fc',
    margin: 4,
    alignItems: 'center',
    justifyContent: 'center',
  },
});

Implementation notes:

  • Dimensions.addEventListener triggers when the device rotates.
  • Recalculating numColumns forces FlatList to re-render with the new layout.
  • Setting explicit width and height ensures square cells that adapt to the new column count.

Key Considerations for React Native Grid Performance

When implementing a react native grid, optimize for these critical concerns:

Concern Implementation Strategy
Item sizing All items in a row must share the same width. Use flex: 1 on the item container or calculate width from Dimensions.get('window').width / numColumns.
Spacing Add ItemSeparatorComponent for vertical gaps and columnWrapperStyle for horizontal row styling.
Key extraction Provide a stable keyExtractor so recycled rows maintain component identity and state.
Performance Keep initialNumToRender low, enable windowSize tuning, and avoid heavy computations inside renderItem.
Orientation changes Re-calculate dimensions on the onLayout event or listen to Dimensions updates to adjust column counts dynamically.

React Source Code Context

While FlatList lives in the React Native repository, the underlying virtualization principles mirror concepts found in the React core codebase. For example, in packages/react-devtools-shared/src/devtools/views/Components/OwnersStack.js, React DevTools renders a flat list of component owners using an itemData array and a renderer receiving {index, style} parameters—demonstrating the same recycling pattern that powers VirtualizedList.

Similarly, packages/use-sync-external-store/src/useSyncExternalStore.js provides the subscription mechanism that keeps virtualized lists synchronized with external data sources, while packages/react-devtools-shared/src/devtools/views/SuspenseTab/SuspenseBreadcrumbs.js illustrates performance-focused list rendering in the DevTools UI.

Summary

  • Use FlatList with numColumns to create a react native grid without external dependencies.
  • Calculate item widths using Dimensions or flex: 1 to ensure equal column distribution.
  • Implement keyExtractor and optimize windowSize for smooth scrolling performance with large datasets.
  • Handle orientation changes by listening to Dimensions events and dynamically updating numColumns.
  • Style rows with columnWrapperStyle and add separators with ItemSeparatorComponent for professional grid spacing.

Frequently Asked Questions

What is the difference between FlatList and the old ListView in React Native?

FlatList is the modern replacement for the deprecated ListView component, offering significant performance improvements through virtualization. Unlike ListView, which rendered all rows immediately, FlatList only mounts items visible on screen plus a configurable window, recycling components as you scroll to maintain 60fps performance even with thousands of items.

How do I handle different screen sizes in a react native grid?

Calculate your item dimensions dynamically using Dimensions.get('window') and divide by your numColumns value. For responsive layouts that adapt to orientation changes, attach a listener to Dimensions.addEventListener('change', ...) and update both numColumns and item size in your component state, which triggers a re-render with the new layout.

Why are my grid items not aligning properly?

Uneven alignment typically occurs when items in the same row have different heights or when width calculations don't account for margins and padding. Ensure all items in a row share the same width by using flex: 1 or calculated dimensions, and verify that your columnWrapperStyle doesn't conflict with the internal row styling applied by FlatList.

Can I use FlatList for horizontal grids?

FlatList supports horizontal scrolling via the horizontal prop, but the numColumns prop only works with vertical lists. For a horizontal grid (items arranged in rows that scroll sideways), you would need to use ScrollView with a custom layout or a library like react-native-super-grid, as the core FlatList implementation specifically maps numColumns to vertical column layouts.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →