Stack
A flex-based layout primitive for arranging items in a row or column. Configurable direction, gap, alignment, justification, wrapping, reversal, dividers, inline display, polymorphic rendering, and LTR/RTL support. Ships with HStack and VStack convenience wrappers.
Playground
Installation
pnpm add @tessinaui/uiUsage
import { Stack, HStack, VStack } from "@tessinaui/ui";{/* Vertical stack (default) */}
<Stack gap="md">
<div>First</div>
<div>Second</div>
<div>Third</div>
</Stack>
{/* Horizontal row */}
<Stack direction="horizontal" gap="sm" align="center">
<Avatar src="/me.png" />
<div>
<p className="font-medium">Jane Doe</p>
<p className="text-muted-foreground">jane@example.com</p>
</div>
</Stack>
{/* Convenience wrappers */}
<HStack gap="sm">…</HStack>
<VStack gap="md">…</VStack>
{/* Dividers between items */}
<Stack
direction="horizontal"
align="center"
divider={<span className="w-px self-stretch bg-border" />}
>
<span>Home</span>
<span>Products</span>
<span>About</span>
</Stack>
{/* Wrapping with reverse order */}
<Stack direction="horizontal" wrap reverse gap="sm">
{tags.map((t) => <Chip key={t}>{t}</Chip>)}
</Stack>
{/* Polymorphic — render as <nav> */}
<Stack
direction="horizontal"
gap="sm"
render={<nav aria-label="Primary" />}
>
<a href="/">Home</a>
<a href="/about">About</a>
</Stack>
{/* RTL */}
<Stack direction="horizontal" dir="rtl" gap="sm">
<span>أول</span>
<span>ثاني</span>
<span>ثالث</span>
</Stack>Showcase
Direction
Switch between a column and a row with direction.
| Value | Flex mapping |
|---|---|
"horizontal" | flex-row |
"vertical" (default) | flex-col |
Combine with reverse to flip item order.
direction | reverse | Result |
|---|---|---|
"horizontal" | false | flex-row |
"horizontal" | true | flex-row-reverse |
"vertical" | false | flex-col |
"vertical" | true | flex-col-reverse |
Gap
Controls the spacing between items (and between items and dividers when present).
| Value | Tailwind | px |
|---|---|---|
"none" | gap-0 | 0 |
"xs" | gap-1 | 4 |
"sm" | gap-2 | 8 |
"md" (default) | gap-4 | 16 |
"lg" | gap-6 | 24 |
"xl" | gap-8 | 32 |
"2xl" | gap-12 | 48 |
Align (cross-axis)
Maps to flex align-items. In a vertical stack, this controls the horizontal alignment of children.
| Value | Flex mapping |
|---|---|
"start" | items-start |
"center" | items-center |
"end" | items-end |
"stretch" (default) | items-stretch |
"baseline" | items-baseline |
Justify (main-axis)
Maps to flex justify-content.
| Value | Flex mapping |
|---|---|
"start" (default) | justify-start |
"center" | justify-center |
"end" | justify-end |
"between" | justify-between |
"around" | justify-around |
"evenly" | justify-evenly |
Wrap
wrap={true} enables flex-wrap so items fall to a new line when the container cannot fit them on the main axis. Useful for chip groups, tag clouds, or responsive toolbars.
Divider
Pass any node to divider to render it between each child. Stack handles the interleaving — you just describe the separator.
<Stack
direction="horizontal"
align="center"
gap="md"
divider={<span className="w-px self-stretch bg-border" />}
>
<span>Alpha</span>
<span>Beta</span>
<span>Gamma</span>
</Stack>For vertical stacks, render a horizontal rule instead:
<Stack
gap="md"
divider={<span className="h-px self-stretch bg-border" />}
>…</Stack>Inline
inline={true} swaps flex for inline-flex so the stack can sit inline with surrounding text or flow alongside other inline-block elements.
HStack and VStack
Direction-locked convenience wrappers that preserve the full API otherwise.
<HStack gap="sm">…</HStack> {/* direction="horizontal" */}
<VStack gap="md">…</VStack> {/* direction="vertical" */}They accept every Stack prop except direction.
Polymorphic render
Keep Stack styling while swapping the underlying element for semantic HTML.
<Stack direction="horizontal" gap="sm" render={<nav aria-label="Primary" />}>
<a href="/">Home</a>
<a href="/about">About</a>
</Stack>
<Stack gap="sm" render={<ul />}>
<li>First</li>
<li>Second</li>
</Stack>The passed element's existing className is merged with Stack's generated classes, and its own children (if any) are preserved over the Stack's children.
RTL
dir="rtl" flips the main-axis direction of horizontal stacks and is forwarded to the underlying element. Per-component dir means you can mix LTR and RTL stacks on the same page without changing the global document direction.
Accessibility
-
Stack is a structural layout primitive — it does not apply any ARIA roles on its own.
-
Use
renderto wrap semantic HTML (<nav>,<ul>,<section>,<header>) when the grouping has meaning. Keep<div>(the default) for purely presentational arrangements. -
Dividers are cosmetic. Mark them as
aria-hiddenso they are not announced by screen readers:divider={<span aria-hidden className="w-px self-stretch bg-border" />} -
focus-visiblerings on Stack's children remain visible because Stack sets no overflow clipping.
API Reference
Stack Props
| Prop | Type | Default | Description |
|---|---|---|---|
direction | "horizontal" | "vertical" | "vertical" | Axis on which children flow |
reverse | boolean | false | Reverses child order via flex-*-reverse |
gap | "none" | "xs" | "sm" | "md" | "lg" | "xl" | "2xl" | "md" | Space between children |
align | "start" | "center" | "end" | "stretch" | "baseline" | "stretch" | Cross-axis alignment (align-items) |
justify | "start" | "center" | "end" | "between" | "around" | "evenly" | "start" | Main-axis distribution (justify-content) |
wrap | boolean | false | Enables flex-wrap |
inline | boolean | false | Uses inline-flex instead of flex |
divider | ReactNode | — | Node rendered between each direct child |
render | ReactElement | — | Polymorphic target — clones this element and applies Stack styling to it |
dir | "ltr" | "rtl" | — | Text / flow direction, forwarded to the DOM node |
className | string | — | Additional classes on the root element |
HStack / VStack
Both accept every prop above except direction, which is locked to "horizontal" and "vertical" respectively.
Notes
- Default direction is vertical. This matches the convention of most React layout primitives (Chakra, Radix Themes) and reads naturally in JSX where children are typed top-to-bottom.
align="stretch"is the default. In a column, children expand to fill the horizontal width. Switch to"start","center", or"end"to let children size themselves.- Divider interleaving uses
React.Children.toArray, so keyed lists (.map()output) work without warnings — Stack assigns its own keys to the divider fragments. renderprecedence: when passed, Stack renders the provided element type with its original props merged in, its originalclassNameconcatenated with Stack's generated classes, and its original children (if any) preserved over the Stack'schildren.- No SSR caveats. Stack is a pure layout primitive with no hooks beyond
useMemofor divider interleaving — safe to render on the server.
Container
A layout primitive that constrains content to a maximum width, applies consistent horizontal padding, and centers it within the viewport. Seven sizes, seven padding presets, polymorphic rendering, and RTL support.
Flex
A low-level flexbox layout primitive that exposes the full flex API — four directions, three wrap modes, independent row and column gap, align-items, justify-content, align-content, inline mode, polymorphic render, and LTR/RTL. Ships with FlexItem for per-child grow, shrink, basis, order, and alignSelf control.