Stack

A 1D flexbox layout shorthand for arranging children vertically or horizontally with consistent spacing, alignment, and wrapping controls.

Installation

npm install @designforge/ui

Usage

import { Stack, stackVariants } from "@designforge/ui";

Examples

Vertical Stack (Default)

The default Stack flows children top-to-bottom with gap={4}.

import { Stack } from "@designforge/ui";
 
export default function VerticalStack() {
  return (
    <Stack>
      <div className="p-3 border rounded-md">First item</div>
      <div className="p-3 border rounded-md">Second item</div>
      <div className="p-3 border rounded-md">Third item</div>
    </Stack>
  );
}

Horizontal Stack

Set direction="horizontal" to arrange children in a row.

import { Stack } from "@designforge/ui";
 
export default function HorizontalStack() {
  return (
    <Stack direction="horizontal" gap={4}>
      <div className="px-4 py-2 bg-muted rounded-md">Alpha</div>
      <div className="px-4 py-2 bg-muted rounded-md">Beta</div>
      <div className="px-4 py-2 bg-muted rounded-md">Gamma</div>
    </Stack>
  );
}

Centered Stack

Use align="center" and justify="center" together to center children on both axes.

import { Stack } from "@designforge/ui";
 
export default function CenteredStack() {
  return (
    <Stack align="center" justify="center" className="h-40 border rounded-xl">
      <p className="text-sm font-medium">Centered content</p>
      <p className="text-xs text-muted-foreground">Both axes aligned</p>
    </Stack>
  );
}

Justify Between

Use justify="between" in a horizontal stack to push items to the edges.

import { Stack } from "@designforge/ui";
 
export default function JustifyBetween() {
  return (
    <Stack direction="horizontal" justify="between" align="center" className="w-full px-4 py-3 border rounded-lg">
      <span className="font-medium text-sm">Section Title</span>
      <button className="text-sm text-primary hover:underline">View all</button>
    </Stack>
  );
}

Navigation Links

A horizontal Stack is a clean way to build a top-level navigation bar.

import { Stack } from "@designforge/ui";
 
export default function NavLinks() {
  const links = ["Products", "Pricing", "Docs", "Blog", "Support"];
 
  return (
    <nav>
      <Stack direction="horizontal" gap={1} align="center">
        {links.map((link) => (
          <a
            key={link}
            href="#"
            className="px-3 py-2 rounded-md text-sm hover:bg-muted transition-colors"
          >
            {link}
          </a>
        ))}
      </Stack>
    </nav>
  );
}

Form Fields

Vertically stacked form fields with controlled spacing.

import { Stack } from "@designforge/ui";
 
export default function FormFields() {
  return (
    <form className="w-80">
      <Stack gap={5}>
        <div className="space-y-1.5">
          <label htmlFor="name" className="text-sm font-medium">Full name</label>
          <input
            id="name"
            type="text"
            placeholder="Jane Smith"
            className="w-full px-3 py-2 border rounded-md text-sm"
          />
        </div>
        <div className="space-y-1.5">
          <label htmlFor="email" className="text-sm font-medium">Email</label>
          <input
            id="email"
            type="email"
            placeholder="jane@example.com"
            className="w-full px-3 py-2 border rounded-md text-sm"
          />
        </div>
        <div className="space-y-1.5">
          <label htmlFor="message" className="text-sm font-medium">Message</label>
          <textarea
            id="message"
            rows={4}
            placeholder="Your message..."
            className="w-full px-3 py-2 border rounded-md text-sm resize-none"
          />
        </div>
        <button
          type="submit"
          className="w-full py-2 bg-primary text-primary-foreground rounded-md text-sm font-medium"
        >
          Send message
        </button>
      </Stack>
    </form>
  );
}

Card List

Map a data array into a vertically stacked list of cards.

import { Stack } from "@designforge/ui";
 
const users = [
  { name: "Alice Chen", role: "Design Lead" },
  { name: "Marcus Webb", role: "Frontend Engineer" },
  { name: "Priya Nair", role: "Product Manager" },
];
 
export default function CardList() {
  return (
    <Stack gap={3} className="w-72">
      {users.map((user) => (
        <div
          key={user.name}
          className="flex items-center gap-3 p-3 border rounded-lg hover:bg-muted/50 transition-colors"
        >
          <div className="h-9 w-9 rounded-full bg-muted flex items-center justify-center font-medium text-sm">
            {user.name[0]}
          </div>
          <div>
            <p className="text-sm font-medium">{user.name}</p>
            <p className="text-xs text-muted-foreground">{user.role}</p>
          </div>
        </div>
      ))}
    </Stack>
  );
}

Mixed Gap

Nest Stacks with different gap values to achieve hierarchy in spacing.

import { Stack } from "@designforge/ui";
 
export default function MixedGap() {
  return (
    <Stack gap={8} className="w-80">
      {/* Tight group */}
      <Stack gap={2}>
        <p className="text-xs font-semibold text-muted-foreground uppercase tracking-wider">
          Recent
        </p>
        <div className="p-3 border rounded-md text-sm">Invoice #1042</div>
        <div className="p-3 border rounded-md text-sm">Invoice #1041</div>
      </Stack>
 
      {/* Loose group */}
      <Stack gap={6}>
        <p className="text-xs font-semibold text-muted-foreground uppercase tracking-wider">
          Archived
        </p>
        <div className="p-3 border rounded-md text-sm">Invoice #1040</div>
        <div className="p-3 border rounded-md text-sm">Invoice #1039</div>
      </Stack>
    </Stack>
  );
}

API Reference

PropTypeDefaultDescription
direction"vertical" | "horizontal""vertical"Controls whether children are arranged in a column or a row.
gap0 | 1 | 2 | 3 | 4 | 5 | 6 | 8 | 10 | 124Spacing between children using Tailwind gap tokens.
align"start" | "center" | "end" | "stretch"Cross-axis alignment (align-items). Defaults to browser default when unset.
justify"start" | "center" | "end" | "between"Main-axis justification (justify-content). Defaults to browser default when unset.
wrapbooleanfalseWhen true, children wrap onto new lines when the container is too narrow.
classNamestringAdditional Tailwind or custom classes applied to the Stack container.
refReact.Ref<HTMLDivElement>Forwarded ref to the underlying <div> element.

Accessibility

  • Stack renders a plain <div> with no implicit ARIA role. Its purpose is purely compositional layout.
  • Avoid using Stack as the sole semantic structure for content that requires a list relationship. When children are semantically a list, use a <ul> or <ol> with <li> elements and apply Stack styling through className or wrap the list element.
  • Nesting Stacks deeply does not introduce any additional accessibility concerns, as each is invisible to assistive technology as a structural container.
  • When using direction="horizontal" for navigation, wrap the Stack inside a <nav> element and ensure each child link has a descriptive label.

Live View

Open in Storybook ↗