For the complete documentation index, see llms.txt. Markdown variants are available by appending .md to any URL or sending an Accept: text/markdown header. An agent skill is available at /.well-known/agent-skills/site-skill.md.
1
Support

PageHeader

A responsive dashboard heading with supporting content, breadcrumbs, status, and actions.

Playground

Live preview

Projects

Manage your workspace projects.
Breadcrumb and status controls use plain text. Supply your own navigation or badge in an app. Create project is a sample link; replace /projects/new with your route. Show actions changes demo composition, not a public prop.
Customize PageHeader
Customize
pageheader-demo.tsx
import { PageHeader } from "@/components/ui/page-header";

export const PageHeaderDemo = () => (
  <PageHeader
    title={"Projects"}
    description={"Manage your workspace projects."}
    actions={
      <a href="/projects/new" className="inline-flex min-h-9 items-center rounded-md border border-border px-3 text-sm font-medium text-foreground hover:bg-muted focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring">
        Create project
      </a>
    }
  />
);

Installation

Initialize shadcn in your React project, then install this component from the Vandor UI registry:

$ pnpm dlx shadcn@latest add https://vandor-ui.vercel.app/r/page-header.json

Dependencies

Installed automatically by the registry CLI. For manual setup:

Dependencies1 required packages
  • cn
$ pnpm add cn

Usage

import { PageHeader } from "@/components/ui/page-header";
 
<PageHeader title="Projects" description="Manage your workspace projects." />;

PageHeader provides the identity and primary actions of a dashboard page. It is not the app's global navigation bar. Keep filters, tabs, summaries, and page content outside the header.

Examples

Simple heading

Projects

Manage your workspace projects.
page-header-demo.tsx
import { PageHeader } from "@/components/ui/page-header";

export const PageHeaderDemo = () => (
  <div className="w-full min-w-0">
    <PageHeader
      title="Projects"
      description="Manage your workspace projects."
    />
  </div>
);

Only title is required. The component renders an h1, supporting content, and a bottom divider. It adds no outer margin, background, width cap, sticky positioning, state, or animation. Remove the divider with className="border-b-0" if the surrounding layout already provides one.

Dashboard composition

Projects

3 active
Manage access and review your active projects.

Your project list belongs here, outside the header.

page-header-dashboard-demo.tsx
import Link from "next/link";

import { PageContainer } from "@/components/ui/page-container";
import { PageHeader } from "@/components/ui/page-header";

export const PageHeaderDashboardDemo = () => (
  <PageContainer className="flex flex-col gap-6">
    <PageHeader
      title="Projects"
      description="Manage access and review your active projects."
      breadcrumb={
        <nav aria-label="Breadcrumb">
          <ol className="flex flex-wrap items-center gap-2">
            <li>
              <Link href="/workspace" className="underline underline-offset-4">
                Workspace
              </Link>
            </li>
            <li aria-hidden="true">/</li>
            <li aria-current="page">Projects</li>
          </ol>
        </nav>
      }
      status={
        <span className="rounded-md bg-muted px-2 py-1 text-xs">3 active</span>
      }
      actions={
        <>
          <Link
            href="/projects/export"
            className="inline-flex min-h-9 items-center rounded-md border border-border px-3 text-sm font-medium hover:bg-muted focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring"
          >
            Export
          </Link>
          <Link
            href="/projects/new"
            className="inline-flex min-h-9 items-center rounded-md bg-primary px-3 text-sm font-medium text-primary-foreground hover:bg-primary/90 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring"
          >
            Create project
          </Link>
        </>
      }
    />
    <p className="text-sm text-muted-foreground">
      Your project list belongs here, outside the header.
    </p>
  </PageContainer>
);

Install PageContainer separately for this example. It sets the page width and gutters; className="flex flex-col gap-6" provides spacing between the header and content. Example links are placeholders for your app's routes, not routes supplied by PageHeader.

The breadcrumb slot accepts your existing breadcrumb component without adding another navigation landmark. Supply a named nav, an ordered list, and aria-current="page" where appropriate. Plain text, as used by the playground control, is not automatically breadcrumb navigation.

Use status for a badge or other contextual content alongside the heading. Use actions for one or more links or buttons; fragments work without an extra wrapper. Actions follow the copy in DOM order. Below Tailwind's md breakpoint (768px by default), they appear below the heading and align to the start. On wider screens they align to the trailing side and wrap when needed. Breakpoint selection follows the viewport, not the width of an embedded preview.

Consumer setup and optional stories

Requires React 19, Tailwind CSS 4, and cn. Load your global Tailwind stylesheet with the semantic border and muted-foreground tokens. Example content and stories also use muted, foreground, primary, primary-foreground, and ring. There are no automatic Button, Badge, Breadcrumb, or PageContainer registry dependencies; supply or install your chosen components separately.

PageHeader has no provider, browser API, or client directive. It can be imported into a Next.js Server Component; interactive actions need their own appropriate client boundary. Installation follows the UI alias in components.json. For manual setup, install cn and copy the Source into your UI directory. Review overwrite prompts before replacing an existing PageHeader. Examples and playground adapters are not distributed.

Install PageHeader first, then optionally install its portable stories:

$ pnpm dlx shadcn@latest add https://vandor-ui.vercel.app/r/page-header-stories.json

Use your existing @storybook/react installation with matching Storybook package versions (types and composition checked with 10.6.1). Discover **/*.stories.tsx in your UI directory and load your global Tailwind/theme stylesheet in the preview. No extra providers or addons are required. The stories-only item does not reinstall PageHeader or modify Storybook configuration. This project has no Storybook server; Storybook UI appearance is not verified here.

Behavior & Accessibility

The root is a native header, with attributes, handlers, and React 19 ref forwarded. title is the required heading text, not the native HTML tooltip attribute. Native children is not supported: use the named slots instead. Descriptions render inside a div so rich content such as a paragraph does not create invalid nested paragraphs.

Use one PageHeader as the page's primary h1 inside its main content. A header scoped inside main is not the global banner. The component does not label your main landmark, announce changing statuses, or move focus; add appropriate application-level accessibility where needed. Empty strings, null, undefined, and booleans omit optional slot wrappers; numeric zero remains visible. Empty fragments and arrays are not inspected for content.

Titles and descriptions wrap rather than truncate. Arbitrary wide custom slot content must provide its own responsive layout. Prefer actions directly inside a fragment so the actions container can wrap them. Preserve meaningful action names and native link/button semantics.

The playground's showActions is a composition control, not a public prop. Generated code uses the same text, optional slots, and sample action link as the preview. Reset restores these settings. The fixed sample link demonstrates layout, not project creation behavior.

Props

Component props, types, defaults, and descriptions
PropType / DescriptionDefault
actionsReactNode

One or more action elements, trailing on desktop and below the heading on mobile. Wraps when space is limited.

Not set
breadcrumbReactNode

Content above the heading. Supply your own accessible breadcrumb navigation; PageHeader adds no nav wrapper.

Not set
classNamestring

Classes merged on the header root. No outer margin is added; border-b-0 removes the divider.

Not set
descriptionReactNode

Supporting content below the heading, rendered in a div to allow rich content.

Not set
refRef<HTMLElement>

React 19 ref forwarded to the native header element.

Not set
statusReactNode

Optional content next to the title, such as a badge. Does not add a live region.

Not set
titlestring

Page heading rendered as an h1. This is not the native header title attribute.

Required

PageHeader forwards native header attributes, handlers, and React 19 refs except children and title. Use its named slots; title sets the h1 text, not the native tooltip attribute.

Source

import { cn } from "cn";
import type { ComponentProps, ReactNode } from "react";
 
export type PageHeaderProps = Omit<
  ComponentProps<"header">,
  "title" | "children"
> & {
  title: string;
  description?: ReactNode;
  breadcrumb?: ReactNode;
  status?: ReactNode;
  actions?: ReactNode;
};
 
const hasContent = (value: ReactNode) =>
  value !== null &&
  value !== undefined &&
  typeof value !== "boolean" &&
  value !== "";
 
export const PageHeader = ({
  title,
  description,
  breadcrumb,
  status,
  actions,
  className,
  ...props
}: PageHeaderProps) => (
  <header
    data-slot="page-header"
    className={cn(
      "flex min-w-0 flex-col gap-4 border-b border-border pb-6 md:flex-row md:items-end md:justify-between md:gap-6",
      className
    )}
    {...props}
  >
    <div className="flex min-w-0 flex-1 flex-col gap-3">
      {hasContent(breadcrumb) ? (
        <div
          data-slot="page-header-breadcrumb"
          className="min-w-0 text-sm text-muted-foreground"
        >
          {breadcrumb}
        </div>
      ) : null}
      <div className="flex min-w-0 flex-wrap items-center gap-2">
        <h1 className="min-w-0 text-2xl font-semibold tracking-tight wrap-break-word">
          {title}
        </h1>
        {hasContent(status) ? (
          <div data-slot="page-header-status" className="min-w-0 text-sm">
            {status}
          </div>
        ) : null}
      </div>
      {hasContent(description) ? (
        <div
          data-slot="page-header-description"
          className="max-w-[65ch] text-sm leading-relaxed text-muted-foreground wrap-break-word"
        >
          {description}
        </div>
      ) : null}
    </div>
    {hasContent(actions) ? (
      <div
        data-slot="page-header-actions"
        className="flex min-w-0 max-w-full flex-wrap items-center gap-2 md:justify-end"
      >
        {actions}
      </div>
    ) : null}
  </header>
);