Skip to Content
BlocksPageHeaderBlock

PageHeaderBlock

Establish page context with breadcrumbs, title, status and a prioritised action group.

Plan idea 03, category Navigation & shells. Status experimental — see verification.

Import

import { PageHeaderBlock } from "@raydenui/ui/blocks"; import type { PageHeaderAction, PageHeaderBreadcrumb } from "@raydenui/ui/blocks";

Usage

title is required; headingLevel defaults to "h1" because a page header usually owns the page’s only heading. Drop it to "h2" when the shell already provides one.

<PageHeaderBlock eyebrow="Consignment" title="Northwind Logistics" description="Twelve active shipments across four lanes." breadcrumbs={[ { id: "home", label: "Home", href: "/" }, { id: "customers", label: "Customers", href: "/customers" }, { id: "current", label: "Northwind Logistics" }, ]} status={{ label: "On hold", tone: "warning", description: "Awaiting a credit check." }} meta={[ { id: "owner", label: "Owner", value: "Alex Morgan" }, { id: "updated", label: "Updated", value: "2 hours ago" }, ]} actions={[ { id: "new", label: "New shipment", onClick: () => openCreate(), priority: "primary" }, { id: "export", label: "Export", href: "/customers/northwind/export" }, { id: "delete", label: "Delete", unavailableReason: "Only owners can delete a customer." }, ]} />

The last breadcrumb is the current page: it is rendered as plain text with aria-current="page", so it needs neither href nor onClick.

Variant

variant="surface" (the default) includes a background and horizontal padding. Use variant="plain" inside an existing page gutter rather than relying on negative margins.

<PageHeaderBlock title="Overview" variant="plain" />

Action priority and overflow

Primary actions are never moved into the overflow disclosure. maxInlineActions limits how many of the remaining secondary actions stay inline.

<PageHeaderBlock title="Shipments" actions={actions} actionsLabel="Page actions" maxInlineActions={2} overflowLabel="More actions" />

Metadata on small containers

Metadata collapses behind a disclosure below 640px of container width. Set collapseMetaOnSmallScreens={false} where the metadata is essential.

<PageHeaderBlock title="Shipments" meta={meta} collapseMetaOnSmallScreens={false} />

Long breadcrumb trails

The first and the last two crumbs always stay visible; the middle collapses behind a reversible disclosure once the trail is longer than maxVisibleBreadcrumbs.

<PageHeaderBlock title="Line item 4" breadcrumbs={deepTrail} maxVisibleBreadcrumbs={4} breadcrumbExpandLabel={(hidden) => `Show ${hidden} hidden levels`} breadcrumbCollapseLabel={(hidden) => `Hide ${hidden} levels`} />

Loading and error

<PageHeaderBlock title="Northwind Logistics" state="loading" loadingMessage="Loading the page" />
<PageHeaderBlock title="Northwind Logistics" state="error" errorTitle="This page could not load" errorDescription="The customer record is temporarily unavailable." onRetry={() => refetch()} />

Props

PropTypeDefaultDescription
titleReactNode—Required.
headingLevel"h1"–"h6""h1"Drop to "h2" when the shell owns the page heading.
eyebrowReactNode—Label above the title. Not a heading.
descriptionReactNode—Supporting explanation.
breadcrumbsPageHeaderBreadcrumb[]—The last crumb is the current page.
breadcrumbLabelstring"Breadcrumb"Accessible name for the breadcrumb landmark.
maxVisibleBreadcrumbsnumber4Trail length before the middle collapses.
breadcrumbExpandLabel(hiddenCount: number) => string—Spoken label for the expand toggle.
breadcrumbCollapseLabel(hiddenCount: number) => string—Spoken label once expanded.
statusPageHeaderStatus—{ label, tone?, description? }.
metaPageHeaderMetaItem[]—{ id, label, value, icon? }.
metaLabelstring"Page details"Accessible name for the meta list.
collapseMetaOnSmallScreensbooleantrueReveal metadata on demand below 640px of container width.
variant"surface" | "plain""surface"plain fits an existing page gutter.
backActionPageHeaderBackAction—{ label, href?, onClick? }.
actionsPageHeaderAction[]—Prioritised action group.
actionsLabelstring"Page actions"Accessible name for the action group.
maxInlineActionsnumber3Primary actions are never moved.
overflowLabelstring"More actions"Overflow disclosure label.
state"default" | "loading" | "error""default"Lifecycle owned by your application.
errorTitlestring—Shown while state="error".
errorDescriptionstring—Shown while state="error".
onRetry() => void—The retry control is omitted without it.
loadingMessagestring—Announced while loading.
childrenReactNode—Content under the header row, e.g. a tab bar or a filter row.
classNamestring—Lands on the container root, outside the padded surface.

PageHeaderAction

A union of three arms, all sharing { id, label, icon?, priority?, description? } where priority is "primary" | "secondary" | "overflow" (default "secondary"):

ArmExtra membersRenders as
Destinationhref, external?, onClick?a native anchor
ActiononClicka native button
UnavailableunavailableReasona disabled control with a visible reason

PageHeaderBreadcrumb has the same shape without priority, plus a third arm with neither href nor onClick for the current page.

States

  • default — the page header row.
  • loading / error — with an optional retry control.
  • permission-blocked action — a disabled control with a visible reason.

Verification

Measured 22 September 2026 in desktop Chromium: 261 browser tests across 27 story files, light and dark compositions with no document overflow at 320, 390, 768 and 1440 CSS pixels, and screenshots inspected at phone and desktop widths. A composition assertion checks a single h1. Re-run in the repository story suite on 23 September 2026.

Not verified: a screen-reader pass, any non-Chromium engine, operating-system text-size settings, or physical devices.

Known limitations

  • Status tones are neutral, success, warning and danger. There is no info tone: the palette has --color-info-400 and --color-info-500 only, with no ground and no text role that inverts for dark mode, so an info badge could not be made readable in both modes without inventing a token.
  • The block renders its own action recipe rather than composing Button, which cannot render an anchor.
Last updated on