Grid
A responsive layout on twelve columns. GridContainer sets the column count and the gutters; Grid says how many columns a cell takes.
import { Grid, GridContainer } from 'neba';
<GridContainer spacing={3}>
<Grid span={{ xs: 12, md: 8 }}>Body</Grid>
<Grid span={{ xs: 12, md: 4 }}>Sidebar</Grid>
</GridContainer>;GridContainer draws no surface, so it has no variant, color or elevation. Wrap it in a Box or a Card when the sheet is wanted.
Props
GridContainer
| Prop | Type | Default | Description |
|---|---|---|---|
| columns | number | Partial<Record<NebaBreakpoint, number>> | 12 | How many columns a row divides into. Every span and offset inside is read against this number |
| spacing | number | Partial<Record<NebaBreakpoint, number>> | 2 | The gutter between items, on Tailwind's spacing scale: 4 is 1rem. Fractions are allowed: 1.5 is 0.375rem |
| rowSpacing | number | Partial<Record<NebaBreakpoint, number>> | spacing | The gutter between rows only |
| columnSpacing | number | Partial<Record<NebaBreakpoint, number>> | spacing | The gutter between columns only |
| justifyContent | 'start' | 'center' | 'end' | 'space-between' | 'space-around' | 'space-evenly' | 'stretch' | — | How a row distributes the space its items did not use. A prop of its own, not something to reach for sx or className for |
| alignItems | 'start' | 'center' | 'end' | 'stretch' | 'baseline' | 'stretch' | How items sit across the row. The default stretches, so a row of cards is a row of one height |
| alignContent | 'start' | 'center' | 'end' | 'space-between' | 'space-around' | 'space-evenly' | 'stretch' | — | Where the rows sit when the grid is shorter than the box holding it. Only visible on a container with a height of its own |
| wrap | boolean | true | Whether a row that runs out of columns continues on the next one. Off gives one overflowing row, which is what a scrolling strip wants |
| padded | boolean | true | Inner padding. Turn it off when the grid already sits inside something that pads: a Container, a Card, another grid |
| sizeshared | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'md' | The padding's scale. As on Box it never touches a height or the type scale, and it is not the gutter: that is spacing |
| densityshared | 'default' | 'compact' | 'default' | Padding only |
| render | useRender.RenderProp | — | Renders something other than a div (<section />). Base UI's own escape hatch |
| children | ReactNode | — | The Grid items |
Grid
| Prop | Type | Default | Description |
|---|---|---|---|
| span | GridSpan | Partial<Record<NebaBreakpoint, GridSpan>> | a full row | How many of the container's columns the item takes. Per-breakpoint as { xs: 12, md: 6 }. A span wider than the row is clamped to the row rather than overflowing. 'auto' is as wide as the contents and 'grow' takes the space the row has left |
| offset | number | Partial<Record<NebaBreakpoint, number>> | 0 | Columns left empty ahead of the item: space pushed in before it, not an absolute position counted from the start of the row |
| alignSelf | 'auto' | 'start' | 'center' | 'end' | 'stretch' | 'baseline' | — | Overrides the row's alignItems for this item alone |
| render | useRender.RenderProp | — | Renders something other than a div (<li />). Base UI's own escape hatch |
| children | ReactNode | — | What the cell holds |
Both pass every native <div> attribute straight through.
Examples
span
span is read against the container's column count. On the default twelve, span={6} is a half; with columns={24} the same 6 is a quarter.
The width is (100% + gutter) × span / columns − gutter, so twelve span={1} cells and one span={12} end on exactly the same pixel.
auto · grow
Two widths are not a share of twelve. span="auto" is as wide as what is in the cell — an avatar, a chip, a button at the end of a row — and span="grow" is that plus everything the row has left over, which is how a title sits between the two.
Both mix with numbers in one responsive map — the contents' width on a phone, a third of the row from 48rem up:
<Grid span={{ xs: 'auto', md: 4 }}>Reference</Grid>breakpoints
Give span an object and it uses a different value per width. Every entry applies from its own breakpoint up (it is a floor, not a band), which is what makes two of them enough to describe a whole layout.
The widths are Tailwind's own defaults: sm 40rem · md 48rem · lg 64rem · xl 80rem. xs is 0, the value with no media query around it, so a grid and a md: utility change at the same moment.
columns · spacing · rowSpacing · columnSpacing · offset all take responsive values the same way.
spacing · rowSpacing · columnSpacing
spacing is on Tailwind's spacing scale. spacing={4} is 1rem, the same length as gap-4 or Box's p-4. Fractions are allowed, so spacing={1.5} is 0.375rem.
rowSpacing and columnSpacing each override one axis.
columns
The column count. Twelve is the default, but it does not divide by five, so values like 24 are useful. Every span and offset beneath the container is read against this number. A span wider than the row is clamped to the row rather than overflowing.
offset
Empty columns pushed in ahead of the item: space inserted in front of it rather than an absolute position counted from the start of the row. After an item that already took columns, the offset pushes on from there.
justifyContent · alignItems · alignContent · alignSelf
Alignment is set through props rather than a className. The first three live on GridContainer, alignSelf on Grid.
The values are start / center / end, which flip under RTL. Distribution values like space-between keep their CSS spelling.
padded
spacing is the space between items; padded is the padding around the grid. It defaults to true, so turn it off inside a Container, a Card or another grid.
size and density set how much.
Nesting
A grid inside a grid is a GridContainer inside a Grid. The inner grid re-divides the width its cell was given, so span={6} in there is half of a half.