Stack
One-dimensional flex layout with direction, gap, and alignment.
import { Stack } from "@ps1ui/core";Row
<Stack direction="row" gap="md">
<div>01</div>
<div>02</div>
<div>03</div>
</Stack>Column
<Stack direction="column" gap="sm">
<div>01</div>
<div>02</div>
<div>03</div>
</Stack>Responsive direction
<Stack direction={{ base: "column", md: "row" }} gap="md">
<div>01</div>
<div>02</div>
<div>03</div>
</Stack>Semantic element
A layout box is very often the semantic element too — a <nav> of links, the page <main>, a <ul> of rows. Pass as to render that tag instead of the default <div>; every layout prop, the styling and queryContainer behave exactly as they do on a <div>, and the rendered element's own attributes (plus a correctly typed ref) become available on Stack.
as also takes a component, not just a tag name — as={NextLink} forwards the merged class and style to it.
As a navigation landmark
{/* The layout box IS the landmark — no wrapper, no hand-rolled flex CSS. */}
<Stack as="nav" aria-label="Example" direction="row" gap="md">
<Anchor href="/docs">docs</Anchor>
<Anchor href="/blog">blog</Anchor>
<Anchor href="/about">about</Anchor>
</Stack>As a list
- 01
- 02
- 03
<Stack as="ul" gap="sm">
<li>01</li>
<li>02</li>
<li>03</li>
</Stack>Query container
Responsive props resolve against the nearest ancestor that is a container-query context — normally the PS1Root wrapping your app. Pass queryContainer to make aStack that context instead, so its descendants respond to that Stack's own width rather than the page's. Useful for sidebars, split panes and card slots.
It is opt-in because container-type: inline-size erases the element's intrinsic width: an opted-in Stack collapses to zero inside any shrink-to-fit parent (a row Stack, an auto grid track, a float). Give it a definite inline size — an explicit width, flex: 1, or a block-level parent — whenever you turn it on.
Query container
{/* The inner Stack now measures the 320px outer Stack
instead of the page, so it stays on its `base` value. */}
<Stack queryContainer style={{ width: 320 }}>
<Stack direction={{ base: "column", md: "row" }} gap="md">
<div>01</div>
<div>02</div>
<div>03</div>
</Stack>
</Stack>Props
| Prop | Type | Default | Description |
|---|---|---|---|
as | ElementType | "div" | Element or component to render instead of the default <div> — e.g. "nav" / "main" when the layout box also carries page semantics. |
direction | "row" | "column" | "column" | Main-axis direction. |
gap | "none" | "xs" | "sm" | "md" | "lg" | "xl" | "2xl" | "md" | Gap between items on the space scale. |
align | "start" | "center" | "end" | "stretch" | "baseline" | — | Cross-axis alignment (align-items). |
justify | "start" | "center" | "end" | "between" | "around" | "evenly" | — | Main-axis distribution (justify-content). |
wrap | boolean | false | Wrap items onto multiple lines instead of overflowing. |
queryContainer | boolean | false | Make this Stack a container-query context so descendants' responsive props resolve against its width instead of the nearest ancestor container. Costs the Stack its intrinsic width — it collapses to 0 in shrink-to-fit parents (row flex, auto grid track, float), so give it a definite inline size when opting in. |
Also accepts the attributes of the element rendered via as (default: <div>).