Skip to content

Stack

  • A form’s fields, or a card’s lines, down the page
  • A row of buttons, or tags that wrap onto new lines

The space between children belongs to the Stack, not to margins on the children. A CSS reset can’t collapse it, and two margins never add up.

Choosing between laying out:

ComponentUse it forFor example
StackChildren in one line, down or across, with a gap between themA form’s fields, a row of buttons, tags
GridChildren in equal columns, wrapping into rowsCards, a gallery, a dashboard’s tiles
TableRecords compared across the same columnsInvoices, users, order lines
ToolbarA row of controls reached with the arrow keys, as one Tab stopA text editor’s formatting controls
Project settingsA column of lines, a small step apart.
  • React
  • TypeScript
  • CSS
  • Accessibility
  • Design tokens
Saved
import { Badge, Button, Chip, Stack } from '@arun-dev/ui';
export default function StackBasics() {
return (
// A column — the default — with sections a medium gap apart.
<Stack gap="md" style={{ inlineSize: 'min(100%, 28rem)' }}>
<Stack gap="3xs">
<strong>Project settings</strong>
<span className="text-color-secondary">A column of lines, a small step apart.</span>
</Stack>
{/* A row that wraps: chips move to a new line when they run out of room. */}
<Stack direction="row" gap="2xs" wrap render={<ul role="list" aria-label="Tags" />}>
{['React', 'TypeScript', 'CSS', 'Accessibility', 'Design tokens'].map((tag) => (
<Chip key={tag} render={<li />}>
{tag}
</Chip>
))}
</Stack>
{/* A row with its ends pushed apart, lined up down the middle. */}
<Stack direction="row" justify="between" align="center">
<Badge tone="success">Saved</Badge>
<Stack direction="row" gap="2xs">
<Button>Cancel</Button>
<Button variant="primary">Publish</Button>
</Stack>
</Stack>
</Stack>
);
}

Stack is a flex container. gap takes a step on the spacing scale — 0, 3xs up to 3xl — the --space-* token of the same name.

Stack props
PropTypeDefaultDescription
direction'column' | 'row''column'Down the page, or across it.
gap'0' | '3xs' | '2xs' | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl''sm'Space between the children: the --space-* token of the same name.
align'start' | 'center' | 'end' | 'baseline' | 'stretch''stretch'How children line up across the stack: across a column, or down a row.
justify'start' | 'center' | 'end' | 'between''start'How children are spread along the stack. between pushes the ends apart.
wrapbooleanfalseLets children move onto a new line when they run out of room.
classNamestringConcatenated with the component's own classes, never replacing them.
renderReactElementElement or component to render instead of the default. Props, className, event handlers and ref are merged onto it.
refRef<HTMLElement>Ref to the rendered element. Merged with any ref on the render element.
…restHTMLAttributesAnything else is spread onto the rendered element, so id, aria-*, data-* and event handlers all reach the DOM.
  • It renders a <div>. Use render for the element the content needs: render={<ul role="list" />} for a list, render={<form />} for a form. A Stack drops a list’s markers itself; role="list" keeps it announced as a list in Safari.
  • For a layout that changes at a width, such as a column on phones and a row on wide screens, add your own class with a media query. Or use a Grid with minItemSize, which needs no breakpoints.
  • Without React, the stack and space-* utility classes give a column with a gap.