Skip to content

Grid

Status: stableAccessibility reviewedtenet-ui@0.4.0
Import
import { Grid } from 'tenet-ui';

Also accepts every prop of HTMLAttributes<HTMLDivElement>.

Source: src/components/Grid/Grid.tsx

When to use: laying out equal-width columns that reflow with the viewport — a row of stat cards, a card gallery, a two-column form section, a dashboard of panels. Grid and Stack are the only layout primitives: Stack for one axis (toolbars, form fields, vertical rhythm), Grid for two. Reach for it instead of display: grid with pixel gaps or a hand-written media query.

  • columns is the number of equal tracks, fixed (columns={3}) or per breakpoint (columns={{ base: 1, sm: 2, lg: 4 }}). It is mobile-first: base applies from 0, sm/md/lg switch on at the breakpoint-sm/md/lg tokens (640 / 960 / 1280) and a missing step inherits the previous one. Always give a base (or a plain number) — a grid that is 4-wide on phones is a bug.
  • gap maps to the space scale (condensed=8, normal=16, spacious=24) and applies to both axes; rowGap overrides the row spacing only. Never set a pixel gap or margins between grid children.
  • Use Grid.Item span for a child that should cover several columns (a featured card, a full-width footer row). span accepts the same responsive object, so a sidebar can be span={{ base: 1, md: 2 }}. Don’t let a span exceed the column count at that breakpoint.
  • Tracks are minmax(0, 1fr), so long unbroken content truncates or wraps inside its cell rather than widening the column. Put truncate on the Heading/Text inside, not overflow on the grid.
  • align controls block-axis alignment within a row (stretch makes cards in a row equal height, start keeps short items at the top).
  • Don’t nest a Grid to fake a table — tabular data is a Table/DataTable.
  • <Grid columns={{ base: 1, sm: 2, lg: 4 }} gap="normal">{stats.map(s => <Card key={s.id}>…</Card>)}</Grid>
  • <Grid columns={3}><Grid.Item span={2}>…main…</Grid.Item><aside>…</aside></Grid>
  • <Grid columns={2} gap="condensed" rowGap="normal"> — tight columns, roomier rows, both from the scale.
  • <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 20 }}> — raw grid, off-scale gap, no responsive story.
  • <Grid columns={4}> for a phone-first page — fixed four columns squeeze to nothing at 375px; give it a base.

See also: Stack for one-axis layout, Card for the tiles a grid usually holds; system guideline layout (gap scale, breakpoints, container widths).

7 stories for Grid — open in Storybook

The full matrix (all args, controls, accessibility panel) is in Storybook.

Grid

PropTypeDefaultDescription
columnsnumber | GridResponsive
GridResponsiveValue
1Number of equal-width columns, either fixed or per breakpoint, e.g. { base: 1, sm: 2, lg: 4 }. Each column is minmax(0, 1fr) so long content can't blow the track out.
gap'none' | 'condensed' | 'normal' | 'spacious'
GridGap
normalSpace between rows and columns, from the space scale (condensed=8, normal=16, spacious=24).
rowGap'none' | 'condensed' | 'normal' | 'spacious'
GridGap
Overrides the row spacing only, from the same scale; defaults to gap.
align'start' | 'center' | 'end' | 'stretch'
GridAlign
Block-axis alignment of items within their row (maps to align-items).
childrenReactNode

Grid.Item

PropTypeDefaultDescription
spannumber | GridResponsive
GridResponsiveValue
Number of columns this item spans (grid-column: span N), fixed or per breakpoint.
childrenReactNode

GridResponsive

A value per breakpoint. base applies from 0 up; sm/md/lg apply from breakpoint-sm (640), breakpoint-md (960) and breakpoint-lg (1280) upward. Missing steps inherit the previous one (lg → md → sm → base).

MemberTypeDefaultDescription
basenumberValue from 0 up (mobile-first).
smnumberValue from breakpoint-sm (640px) up.
mdnumberValue from breakpoint-md (960px) up.
lgnumberValue from breakpoint-lg (1280px) up.