Skip to content
Guide

Build a page with actions and a sidebar.

Compose Page.Header, Page.Actions, and Page.Body into a React detail screen. Keep the content first in reading order while the summary moves beside it at larger widths.

Responsive asideContent-first DOMNative anchor action
01 / Prerequisites

Install the page frame

Start with React 19, TypeScript, Tailwind CSS 4, and an initialized shadcn project. Register the @layout-kit namespace, then install Page, Stack, and the shadcn card and button components.

Terminal
npx shadcn@latest add @layout-kit/page @layout-kit/stack
npx shadcn@latest add card button

The installation guide covers aliases and registry setup. This example uses buttonVariants to style an actual in-page link; it needs no router, backend, or save handler.

02 / Composition

Choose the page regions

Page constrains width and adds gutters. Page.Header provides a title, description, and action area. Page.Body holds the main region and an optional labeled aside.

  • Use size="lg" for the outer frame, with the default maximum width of 80rem.
  • The sm aside width is 16rem. asideBreakpoint="lg" places it beside the content from Tailwind’s lg breakpoint.
  • asideCollapse="stack" puts the summary after the content below lg. Choose hide only for genuinely redundant information.
  • asidePosition="start" can move the summary to the left on large screens while the DOM remains content-first.
03 / Copy and adapt

Complete example

The action navigates to the delivery checklist. Replace the sample brief and summary with your own content, and give each instance unique fragment IDs if you render several on one page.

Storefront redesign

Project brief and delivery checklist

Make product discovery simpler while keeping the existing checkout.

Delivery checklist

  • Review product navigation
  • Check small-screen layouts
  • Test keyboard navigation
components/project-page.tsx
import { Page } from "@/components/ui/page";
import { Stack } from "@/components/ui/stack";
import { buttonVariants } from "@/components/ui/button";
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";

export function ProjectPage({ titleAs = "h1" }: { titleAs?: "h1" | "h2" }) {
  const SectionTitle = titleAs === "h1" ? "h2" : "h3";
  return (
    <Page size="lg">
      <Page.Header
        title="Storefront redesign"
        titleAs={titleAs}
        description="Project brief and delivery checklist"
        actions={
          <Page.Actions>
            <a href="#project-checklist" className={buttonVariants({ variant: "outline" })}>
              View checklist
            </a>
          </Page.Actions>
        }
      />
      <Page.Body
        asideWidth="sm"
        asideBreakpoint="lg"
        asideCollapse="stack"
        asideLabel="Project summary"
        aside={
          <Card>
            <CardHeader>
              <CardTitle>Project summary</CardTitle>
              <CardDescription>Sample project details</CardDescription>
            </CardHeader>
            <CardContent>
              <p>Owner: Product team. Status: In progress.</p>
            </CardContent>
          </Card>
        }
      >
        <Stack gap={6}>
          <p>Make product discovery simpler while keeping the existing checkout.</p>
          <Stack render={<section aria-label="Delivery checklist" />} gap={3}>
            <SectionTitle
              id="project-checklist"
              className="scroll-mt-24 text-lg font-semibold"
              tabIndex={-1}
            >
              Delivery checklist
            </SectionTitle>
            <Stack render={<ul />} gap={2}>
              <li>Review product navigation</li>
              <li>Check small-screen layouts</li>
              <li>Test keyboard navigation</li>
            </Stack>
          </Stack>
        </Stack>
      </Page.Body>
    </Page>
  );
}
04 / Semantics

Headings and reading order

  • The standalone component defaults to an h1; the preview passes h2 to preserve the guide’s own h1.
  • Page renders a div by default. Place it inside your application’s main landmark; do not create a second main for a nested screen.
  • Page.Header also renders a div. Use render only when a different element is appropriate for your document structure.
  • Page.Body renders a labeled aside after the content. Visual placement never changes screen-reader or keyboard reading order.
  • Test the anchor with a keyboard and zoom to 200%. Keep all essential information available when the aside stacks.

Continue with the full Page API or add a responsive dashboard grid.