Stack
Vertical flex layout with consistent gap, alignment, and justification tokens.
Overview
Stack is a vertical layout primitive: a <div> preconfigured as flex flex-col with three token-driven variants - gap, align, justify. Use it any time you have a list of items flowing top-to-bottom: card contents, form sections, settings rows, vertical menus, list pages, footer columns.
Stack exists to kill ad-hoc flex flex-col gap-X items-Y strings. Once gap, alignment, and justification belong to enums tied to design tokens, the whole codebase stays consistent through token rewrites instead of regex sweeps. Reach for plain <div> only when none of the three axes need configuration.
Preview
'use client';import { Stack } from '@gremorie/rx-containers';export function StackPreview() { return ( <Stack gap="md" className="max-w-sm"> <div className="rounded-md border p-3 text-sm">Item one</div> <div className="rounded-md border p-3 text-sm">Item two</div> <div className="rounded-md border p-3 text-sm">Item three</div> </Stack> );}Installation
bash npx gremorie@latest add rx-stack bash pnpm dlx gremorie@latest add rx-stack bash yarn dlx gremorie@latest add rx-stack bash bunx --bun gremorie@latest add rx-stack Usage
import { Stack } from "@gremorie/rx-containers";
export function SettingsRow({ children }) {
return (
<Stack gap="sm" align="stretch">
{children}
</Stack>
);
}Angular edition planned for a follow-up release. The token enums will map one-to-one to a structural directive plus host bindings.
API
<Stack>
Renders a <div> with flex flex-col plus the gap, alignment, and justification classes derived from the variant enums.
| Prop | Type | Default | Description |
|---|---|---|---|
gap | "none" | "xs" | "sm" | "md" | "lg" | "xl" | "2xl" | "md" | Vertical spacing between children. Maps to gap-0, gap-1, gap-2, gap-4, gap-6, gap-8, gap-12 so values match the project spacing scale. |
align | "start" | "center" | "end" | "stretch" | "baseline" | "stretch" | Cross-axis alignment, applied as items-*. Most card and form contexts want stretch; vertical menus and modal columns want start. |
justify | "start" | "center" | "end" | "between" | "around" | "evenly" | "start" | Main-axis alignment, applied as justify-*. Use between when the last child should pin to the bottom of a height-bounded Stack. |
className | string | - | Extra classes merged after the variant classes. Use for layout escape hatches (min-h-0, flex-1, mx-auto); avoid overriding the variant classes themselves. |
ref | Ref<HTMLDivElement> | - | Forwarded ref to the underlying <div>. |
...props | React.ComponentPropsWithoutRef<"div"> | - | Standard div attributes. |
Vertical only. Stack has no direction prop. For horizontal flow, pair it
with a row helper (or plain flex flex-row gap-*). For two-axis grids, reach
for a dedicated Grid primitive.
Composition
<Stack>is the column. Pickgapto match the rhythm of the surface (cards usually wantmd, dense settings lists wantsm, vertical menus wantxs).- Children can be any element. Stack does not wrap them - it only configures the parent flex container.
- Inside a Stack, nested Stacks compose naturally: each one chooses its own
gapwhile inheriting the cross-axis alignment from the parent. - Footer pinning is the canonical
justify="between"use case: header at top, content in the middle, footer at the bottom of a fixed-height Stack.
Variations
Default rhythm
<Stack gap="md">
<SettingsRow label="Profile" />
<SettingsRow label="Notifications" />
<SettingsRow label="Billing" />
</Stack>The default. gap="md" matches the body text rhythm used elsewhere in the surface.
Centered column
<Stack gap="sm" align="center">
<Badge variant="secondary">{tag}</Badge>
<p className="text-sm text-muted-foreground">
{results.length} matching results
</p>
</Stack>Use align="center" for empty states, callouts, and any column where children should hug the centerline.
Pinned footer
<Stack gap="md" justify="between" className="h-full">
<Stack gap="sm">
<h2 className="text-lg font-semibold">Invite teammates</h2>
<p className="text-sm text-muted-foreground">
Send a magic link or copy the workspace URL.
</p>
</Stack>
<Button>Send invite</Button>
</Stack>Use justify="between" plus a height constraint to keep the CTA stuck to the bottom regardless of the body length.
Accessibility
- Presentation only: Stack is a layout primitive. It adds no ARIA role and no live-region behaviour.
- Semantic children: wrap Stack contents in the right element for the context -
<section>for a landmark,<ul>plus<li>for an actual list,<nav>for vertical menus. - Reading order matches DOM order: Stack is
flex-col, never reversed. Tab order and screen-reader order follow source order. - No injected motion: no transitions, no autofocus, no scroll behaviour. Compose with
Skeleton,Progress, or motion primitives where needed.
Related
- Card - the most common host for a vertical Stack of content.
- Separator - drop between Stack children for explicit dividers.
- ScrollArea - wrap a Stack when the column outgrows its container.