Skip to main content

Grid

A multi-column grid that wraps children into equal-width columns. Built on top of Row, so all Box spacing and alignment props are available.

Usage

import { Card, Grid, Typography } from '@rootnative/components'
import { ThemeProvider } from '@rootnative/core'
import { SafeAreaProvider } from 'react-native-safe-area-context'
import { View } from 'react-native'

export default function App() {
return (
<SafeAreaProvider>
<ThemeProvider>
<View style={{ flex: 1, justifyContent: 'center', padding: 16 }}>
<Grid columns={2} gap="sm">
<Card variant="outlined"><Typography>Item 1</Typography></Card>
<Card variant="outlined"><Typography>Item 2</Typography></Card>
<Card variant="outlined"><Typography>Item 3</Typography></Card>
<Card variant="outlined"><Typography>Item 4</Typography></Card>
</Grid>
</View>
</ThemeProvider>
</SafeAreaProvider>
)
}

How It Works

Each child is wrapped in a cell with flexBasis: ${100 / columns}%, so all columns are equal width. The gap prop is implemented as gutters rather than by shrinking cells: each cell takes half the gap as horizontal padding, and the row cancels the outer half on both edges with a negative margin. flexBasis stays exactly 100 / columns%, so the row occupies the full width of its parent and the outermost cells stay flush with its edges.

Different Column Counts

import { Button, Grid, IconButton, Typography } from '@rootnative/components'
import { ThemeProvider } from '@rootnative/core'
import { SafeAreaProvider } from 'react-native-safe-area-context'
import { View } from 'react-native'

export default function App() {
return (
<SafeAreaProvider>
<ThemeProvider>
<View style={{ flex: 1, justifyContent: 'center', padding: 16, gap: 24 }}>
<Typography variant="titleSmall">Two columns</Typography>
<Grid columns={2} gap="md">
<Button variant="filled" onPress={() => {}}>A</Button>
<Button variant="outlined" onPress={() => {}}>B</Button>
</Grid>
<Typography variant="titleSmall">Four columns</Typography>
<Grid columns={4} gap="sm">
<IconButton icon="heart" accessibilityLabel="Heart" onPress={() => {}} />
<IconButton icon="star" accessibilityLabel="Star" onPress={() => {}} />
<IconButton icon="bell" accessibilityLabel="Bell" onPress={() => {}} />
<IconButton icon="cog" accessibilityLabel="Settings" onPress={() => {}} />
</Grid>
</View>
</ThemeProvider>
</SafeAreaProvider>
)
}

Responsive Columns

columns also accepts a map of window size classes. The grid re-resolves the count when the window crosses a breakpoint. A map cascades down to the nearest smaller breakpoint, so you only set the widths where the count changes.

import { Card, Grid, Typography } from '@rootnative/components'
import { ThemeProvider } from '@rootnative/core'
import { SafeAreaProvider } from 'react-native-safe-area-context'
import { View } from 'react-native'

export default function App() {
return (
<SafeAreaProvider>
<ThemeProvider>
<View style={{ flex: 1, justifyContent: 'center', padding: 16 }}>
<Grid columns={{ compact: 1, medium: 2, expanded: 4 }} gap="sm">
<Card variant="outlined"><Typography>Item 1</Typography></Card>
<Card variant="outlined"><Typography>Item 2</Typography></Card>
<Card variant="outlined"><Typography>Item 3</Typography></Card>
<Card variant="outlined"><Typography>Item 4</Typography></Card>
</Grid>
</View>
</ThemeProvider>
</SafeAreaProvider>
)
}

The map requires a compact key, so every width resolves to a value. Use useBreakpointValue from @rootnative/core directly when a value other than the column count must follow the same breakpoints.

Spanning Cells

A plain child always spans one column. Wrap a child in Grid.Cell to make it span more, for asymmetric layouts such as a main area with a sidebar. The span prop also accepts a breakpoint map, and an oversized span is clamped to the column count, so it fills the row instead of overflowing it.

import { Card, Grid, Typography } from '@rootnative/components'
import { ThemeProvider } from '@rootnative/core'
import { SafeAreaProvider } from 'react-native-safe-area-context'
import { View } from 'react-native'

export default function App() {
return (
<SafeAreaProvider>
<ThemeProvider>
<View style={{ flex: 1, justifyContent: 'center', padding: 16 }}>
<Grid columns={12} gap="sm">
<Grid.Cell span={8}>
<Card variant="outlined"><Typography>Main</Typography></Card>
</Grid.Cell>
<Grid.Cell span={4}>
<Card variant="outlined"><Typography>Aside</Typography></Card>
</Grid.Cell>
<Grid.Cell span={{ compact: 12, medium: 6 }}>
<Card variant="outlined"><Typography>Half on tablets</Typography></Card>
</Grid.Cell>
</Grid>
</View>
</ThemeProvider>
</SafeAreaProvider>
)
}

Grid.Cell is also exported as GridCell for direct imports. It only sizes itself inside a Grid — rendered anywhere else it warns in development and falls back to full width.

Props

PropTypeDefaultRequiredDescription
columnsnumber | BreakpointValues<number>-YesNumber of equal-width columns, or a breakpoint map of column counts. A map cascades down to the nearest smaller breakpoint, so `{ compact: 1, expanded: 4 }` gives 1 column below 840dp and 4 at or above.
wrapbooleanfalseNoWhether children should wrap to the next line when they overflow.
invertedbooleanfalseNoReverses the layout direction (`row-reverse`).
pSpacingValue-NoPadding on all sides
pxSpacingValue-NoHorizontal padding (paddingStart + paddingEnd)
pySpacingValue-NoVertical padding (paddingTop + paddingBottom)
ptSpacingValue-NoPadding top
pbSpacingValue-NoPadding bottom
psSpacingValue-NoPadding start (left in LTR, right in RTL)
peSpacingValue-NoPadding end (right in LTR, left in RTL)
mSpacingValue-NoMargin on all sides
mxSpacingValue-NoHorizontal margin (marginStart + marginEnd)
mySpacingValue-NoVertical margin (marginTop + marginBottom)
mtSpacingValue-NoMargin top
mbSpacingValue-NoMargin bottom
msSpacingValue-NoMargin start
meSpacingValue-NoMargin end
gapSpacingValue-NoGap between children
rowGapSpacingValue-NoRow gap between children
columnGapSpacingValue-NoColumn gap between children
flexnumber-NoFlex value
alignenum-NoAlign items along the cross axis
justifyenum-NoJustify content along the main axis
bgstring-NoBackground color
onKeyDown(event: { nativeEvent: { key?: string; }; }) => void-No-

Grid.Cell

PropTypeDefaultRequiredDescription
spannumber | BreakpointValues<number>1NoNumber of grid columns this cell spans, or a breakpoint map of spans. Clamped to the parent Grid's column count, so an oversized span fills the row instead of overflowing it.
onKeyDown(event: { nativeEvent: { key?: string; }; }) => void-No-