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

> Learn to build a react native grid with FlatList and numColumns. This guide provides a complete implementation for efficient scrollable column layouts.

- Repository: [Meta/react](https://github.com/facebook/react)
- Tags: how-to-guide
- Published: 2026-02-16

---

**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:

```tsx
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`:

```tsx
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:

```tsx
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`](https://github.com/facebook/react/blob/main/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`](https://github.com/facebook/react/blob/main/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`](https://github.com/facebook/react/blob/main/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.