Stack
Vertical layout primitive. Stacks children on the Y axis with a shared gap and optional cross-axis alignment.
Installation
import { Stack } from "@gradeui/ui"Usage
The default top-level layout inside a page or section. Use Stack instead of hand-rolled flex flex-col gap-* so the vertical rhythm is a variant prop the settings panel can edit.
Gap
Vertical spacing between children. Seven steps tied to the spacing scale — none through 2xl.
gap="xs"
gap="md" (default)
gap="xl"
Align
Cross-axis alignment — how each child sits horizontally within the Stack. Default is stretch so children fill the Stack's width.
align="start"
align="center"
align="end"
align="stretch" (default)
asChild
Pass asChild to stamp Stack's layout classes onto an existing semantic element instead of wrapping it in a div. Useful for landmark tags.
<Stack asChild gap="lg">
<section>
<Hero>…</Hero>
<Section>…</Section>
</section>
</Stack>Composition
A common pattern — a centred, narrow column for an auth card or marketing copy.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| gap | "none" | "xs" | "sm" | "md" | "lg" | "xl" | "2xl" | "md" | Vertical gap between children. |
| align | "start" | "center" | "end" | "stretch" | "stretch" | Cross-axis (horizontal) alignment of children. |
| asChild | boolean | false | Render as the single child element via Radix Slot — stamps Stack's layout classes onto an existing semantic tag (e.g. <section>) without nesting a wrapper div. |
| className | string | — | Extra classes merged onto the root element. |
When to use
- As the default top-level layout inside a page's main slot.
- For any vertical list of sections, cards, or form controls.
- Prefer Stack over hand-rolled
flex flex-col gap-*— the spacing becomes a prop editable in the settings panel. - For a horizontal composition, reach for Row instead.
Sidecar
The Markdown sidecar Studio (and the Grade MCP server, when it ships) reads to understand this component — frontmatter, when- to-use guidance, and canonical examples. Authored once at packages/ui/components/ui/stack.md and shipped inside the published @gradeui/ui tarball.
---
name: Stack
import: "@gradeui/ui"
role: layout
props:
- gap?: "none" | "xs" | "sm" | "md" | "lg" | "xl" | "2xl" (default "md") — vertical gap between children
- align?: "start" | "center" | "end" | "stretch" (default "stretch") — cross-axis (horizontal) alignment of children
- justify?: "start" | "center" | "end" | "between" | "around" | "evenly" (default "start") — main-axis (vertical) distribution. Reach for this on absolute-positioned overlays (`justify="end"` pins children to the bottom) and split footers (`justify="between"`).
- asChild?: boolean (default false) — render as the child element via Slot, so `<Stack asChild><section>…</section></Stack>` stamps Stack's classes onto the `<section>` rather than nesting a wrapper div
- className?: string
- children: React.ReactNode
when_to_use: Default top-level layout inside the main slot when composing two or more stacked regions (hero + content + footer, auth card + subtext, etc.). Prefer Stack over hand-rolled `flex flex-col gap-*` so the vertical rhythm is editable through the settings panel.
composes_with: [Section, Row, Split, Hero, any content component]
aliases: [stack, vstack, vertical, column, vertical layout, v-stack, vertical stack, lazyvstack]
---
```jsx
<Stack gap="lg">
<Hero>…</Hero>
<Section>…</Section>
<Section>…</Section>
</Stack>
```
```jsx
// Narrow centred column for auth / marketing copy.
<Stack gap="md" align="center" className="max-w-md mx-auto">
<CardTitle>Sign in</CardTitle>
<Input placeholder="Email" />
<Input placeholder="Password" type="password" />
<Button className="w-full">Continue</Button>
</Stack>
```
```jsx
// Hero overlay pinned to the bottom — use `justify="end"`, NOT
// `className="flex flex-col justify-end"`. Stack is already a flex
// column, so `flex flex-col` in className is dead weight.
<Stack justify="end" gap="md" className="absolute inset-0 p-10 max-w-2xl">
<Badge>Featured</Badge>
<h1 className="text-5xl font-semibold">Severance</h1>
<Button>Play</Button>
</Stack>
```
### Anti-patterns
DO NOT add `flex flex-col` to Stack's className — Stack already applies `flex flex-col` as its base. Same for Row + `flex flex-row`. These are the literal definitions of the primitives.
DO NOT reach for `className="justify-end"` (or `justify-between`, etc.) when the new `justify` prop covers it. Inline-Tailwind layout escapes are how scaffolds slowly drift away from the design system — keep them in props so the settings panel can mutate them.