Stack — spec¶
Synthesized from MUI
Stack. The flexbox-based linear layout primitive — children laid out in a row or column with consistent spacing. Most-used layout container afterBox. The shadcn equivalent uses Tailwind utilities directly (flex flex-col gap-4) — Stack abstracts that for design-system enforcement.
When to use¶
- Any group of elements that need consistent spacing (button rows, form rows, card stacks).
- Replacing
<div style={{ display: 'flex', gap: 16 }}>boilerplate. - For grid-style 2D layouts, use
Gridinstead.
Anatomy¶
Direction = "row":
┌──────┬──[gap]──┬──────┬──[gap]──┬──────┐
│ <1/> │ │ <2/> │ │ <3/> │
└──────┴─────────┴──────┴─────────┴──────┘
Direction = "column":
┌──────┐
│ <1/> │
├─[gap]┤
│ <2/> │
├─[gap]┤
│ <3/> │
└──────┘
API¶
<Stack direction="row" gap={2} alignItems="center" justifyContent="flex-end">
<Button onClick={onCancel}>취소</Button>
<Button onClick={onSave} variant="contained">저장</Button>
</Stack>
| Prop | Type | Default | Description |
|---|---|---|---|
direction |
'row' \| 'column' \| 'row-reverse' \| 'column-reverse' |
'column' |
Flex axis |
spacing / gap |
number \| string |
0 |
Gap between children (theme spacing units; gap={2} = 16px on 8-base) |
alignItems |
'flex-start' \| 'center' \| 'flex-end' \| 'stretch' \| 'baseline' |
— | Cross-axis alignment |
justifyContent |
'flex-start' \| 'center' \| 'flex-end' \| 'space-between' \| 'space-around' |
— | Main-axis alignment |
divider |
ReactNode |
— | Element rendered between children (e.g., <Divider />) |
useFlexGap |
boolean |
true (MUI v6+) |
Use CSS gap instead of margins; safer with divider |
flexWrap |
'nowrap' \| 'wrap' |
'nowrap' |
Wrap children to next line |
sx |
SxProps |
— | Style override (responsive values, custom CSS) |
Responsive¶
States¶
Layout primitive — no interactive states.
Tokens consumed¶
--space-xs /* gap={0.5} */
--space-sm /* gap={1} */
--space-md /* gap={2} */
--space-lg /* gap={3} */
--space-xl /* gap={4} */
Accessibility¶
Stackis a<div>(default). For semantic groupings, override viacomponent:- List of nav items:
component="nav"+ ARIA label. - Toolbar:
component="div" role="toolbar". - For RTL languages,
direction="row"automatically reverses if the document isdir="rtl". For Korean (LTR), no special handling needed.
Edge cases¶
- Mixed-width children with
space-between— Stack distributes remaining space; long children may shrink. Useflex={1}on the spreading child if needed. - Korean text in row direction — Korean labels run wider than Latin equivalents; test row layouts at 320px viewport. Switch to
direction={{ xs: 'column', md: 'row' }}if cramped. - Nested Stacks — common and fine.
Stackdoesn't add semantic meaning, just layout. dividerwithuseFlexGap=false— older MUI versions used margin-based spacing which collides with dividers. SetuseFlexGapexplicitly.
Code example¶
// Card footer with cancel + primary
<Card>
<CardContent>...</CardContent>
<Stack direction="row" gap={1} justifyContent="flex-end" sx={{ p: 2 }}>
<Button onClick={onCancel}>취소</Button>
<Button onClick={onSave} variant="contained">저장</Button>
</Stack>
</Card>
// Form row that stacks on mobile
<Stack direction={{ xs: 'column', sm: 'row' }} gap={2}>
<TextField label="이름" />
<TextField label="사번" />
<TextField label="부서" />
</Stack>
// Vertical list of cards with dividers
<Stack gap={2} divider={<Divider flexItem />}>
{items.map((it) => <Card key={it.id}>{it.title}</Card>)}
</Stack>
Don't¶
- Don't reach for
Stackfor 2D grids — useGrid. - Don't nest 4+ Stacks deep — extract to a custom layout component.
- Don't use
Stack direction="row"for content that should wrap to multiple lines without explicitflexWrap. - Don't apply
Stackto a single child — pure overhead.
References¶
- MUI:
Stack
Cross-reference¶
knowledge/components/INDEX.mdcomponent-box.mdcomponent-grid.mdcomponent-flex.md— alternate flex primitiveknowledge/layout/spacing-and-grid.md