Patterns · Layout
PageHeader
Top of a main view: the single h1, back link or breadcrumbs, status, details, page actions with overflow, and tabs.
With breadcrumb, status and actions
Website refresh
Tasks, owners and dates for this project.
import { Badge, Breadcrumb, Button, PageHeader } from '@rookcom/monarch-react';
<PageHeader
headingLevel="h2"
eyebrow={
<Breadcrumb
items={[
{ label: 'Projects', href: '#/components/page-header' },
{ label: 'Website refresh' },
]}
/>
}
title="Website refresh"
meta={<Badge tone="success">Active</Badge>}
description="Tasks, owners and dates for this project."
actions={
<>
<Button>Share</Button>
<Button variant="primary">New task</Button>
</>
}
/>Usage examples
Publishable entity: back link, status, address and overflow actions
import { Badge, BackLink, Button, CopyButton, PageHeader } from '@rookcom/monarch-react';
// TrashIcon: demo icon from the docs site; use your own.
<PageHeader
headingLevel="h2"
back={<BackLink href="#/components/page-header">All pages</BackLink>}
title="Spring launch"
status={<Badge tone="success">Published</Badge>}
details={
<>
<span>launch.example.com/spring</span>
<CopyButton
value="https://launch.example.com/spring"
iconOnly
size="xs"
label="Copy address"
/>
<span>Updated 5 minutes ago</span>
</>
}
secondaryActions={[
{ id: 'preview', label: 'Preview', href: '#/components/page-header' },
{ id: 'duplicate', label: 'Duplicate', onSelect: () => {} },
{ id: 'unpublish', label: 'Unpublish', onSelect: () => {} },
{
id: 'delete',
label: 'Delete page',
icon: <TrashIcon />,
tone: 'danger',
onSelect: () => {},
},
]}
moreActionsLabel="More actions for Spring launch"
primaryAction={<Button variant="primary">Publish changes</Button>}
/>With section tabs
import { Button, PageHeader, TabsLink, TabsNav } from '@rookcom/monarch-react';
// PlusIcon: demo icon from the docs site; use your own.
<PageHeader
headingLevel="h2"
title="Billing"
description="Plan, invoices and payment methods."
primaryAction={
<Button variant="primary" leadingIcon={<PlusIcon />}>
Add payment method
</Button>
}
tabs={
<TabsNav aria-label="Billing sections">
<TabsLink href="#/components/page-header" active>
Overview
</TabsLink>
<TabsLink href="#/components/page-header">Invoices</TabsLink>
<TabsLink href="#/components/page-header">Payment methods</TabsLink>
</TabsNav>
}
/>Purpose
Lays out the top of every main view: an optional banner, back link and breadcrumbs, the page’s h1 with status and meta, a details line and description, page-level actions (a primary action plus secondary actions that collapse into a "More actions" menu), and section tabs.
Use when
- At the top of each main view, once per page.
- A publishable entity (a page, a domain, a form) needs its status, address and Publish action in one row.
- A page embedded in another shell (App Settings) still needs its heading: use
embedded.
Avoid when
- Headings for sections inside a page: use SectionHeader, SectionCard or SettingsSection.
- The header of one record (a contact, a ticket): use RecordHeader, which adds an avatar, facts and prev/next.
- Dialogs and panels: they have their own headers.
Design principles
- One h1 per page:
titlealways renders as the heading (h1unlessheadingLevel="h2"). - Actions in the header apply to the whole page; keep one primary action and let the rest overflow.
- Slots, not settings: banner, back, breadcrumbs, status, meta, details, actions and tabs take any nodes.
- Responsive by CSS alone: the server and the client render the same markup at every width.
- Print-friendly: actions and tabs are hidden when printing.
API reference
import { PageHeader } from '@rookcom/monarch-react';| Export | Kind | Rendering | Root class |
|---|---|---|---|
PageHeader | component | server-safe | .mn-page-header-sticky-top |
Renders a <header>, forwards its ref and passes native attributes through.
Server-safe: the header has no state. Only the overflow menu (a DropdownMenu) is a client component; secondaryActions with onSelect must come from a client component, href actions work from server components.
PageHeaderAction is { id, label, icon?, onSelect?, href?, disabled?, tone? }.
CSS variables: --mn-page-header-sticky-top (sticky offset, default 0) and --mn-page-header-padding-inline (inline padding, for example the icon-rail offset, default 0).
Generated from the TypeScript declarations of packages/react/src/patterns/layout/page-header.
Properties
PageHeader
Top of every main view: title, context, and the actions that apply to the whole page. Server-safe; only the overflow menu is a client component.
| Prop | Type | Description |
|---|---|---|
title required | ReactNode | Page title, rendered as the page's single h1. |
actions | ReactNode | Primary and secondary actions. Keep one `primary` Button. |
back | ReactNode | A back link above the title: any node (usually a BackLink with `asChild` around a router link). Takes precedence over `backHref`. |
backHref | string | Renders a BackLink to this URL above the title. Use `back` for router links. |
backLabel | ReactNode | Text of the `backHref` link, naming the destination ("All domains"). Default "Back". |
banner | ReactNode | A notice above the header (an Alert or CalloutBanner), for example "This page is archived". |
breadcrumbs | ReactNode | A Breadcrumb above the title. Rendered before `eyebrow` when both are given. |
children | ReactNode | |
className | string | |
description | ReactNode | |
details | ReactNode | A secondary line under the title: an address with a CopyButton, an id, "Updated 5 min ago". Rendered before `description`. |
embedded | boolean | For a page embedded in another shell (App Settings): the title becomes visually hidden but stays the heading, and the outer padding is removed. |
eyebrow | ReactNode | Breadcrumb or eyebrow text above the title. |
headingLevel | "h2" | "h1" | Heading element. `h1` (default) for a page; `h2` when embedded under another h1. |
id | string | |
maxVisibleActions | number | How many secondary actions show as buttons on wide screens. Default 2. |
meta | ReactNode | Badge or status next to the title. |
moreActionsLabel | string | Accessible name of the overflow menu button. Default "More actions". |
primaryAction | ReactNode | The page's main action, placed last in the action row. Stays visible at every width. |
secondaryActions | PageHeaderAction[] | Secondary actions as data. The first `maxVisibleActions` show as buttons on wide screens and the rest go into a "More actions" menu. Below 640px all of them move into the menu. |
status | ReactNode | A status badge next to the title, before `meta` (Published, Draft, Active). |
sticky | boolean | Stick to the top of the scroll container with a blurred backdrop. The offset is `--mn-page-header-sticky-top` (default 0). |
tabs | ReactNode | A TabsList or TabsNav for section navigation below the title. |
Variants
| Prop | Values | Description |
|---|---|---|
banner / back / breadcrumbs / eyebrow / status / meta / details / actions / tabs | 'omitted''provided' | Each slot renders its wrapper only when provided, in that order from the top. |
secondaryActions + maxVisibleActions | '0''1''2 (default)''n' | How many secondary actions show as buttons from 640px; the rest are in the More menu. Below 640px all are in the menu. |
sticky | 'false''true' | Sticks to the top of the scroll container with a blurred, slightly translucent canvas background and a bottom border. Exposed as data-sticky. |
embedded | 'false''true' | Hides the title visually (it stays the heading for screen readers) for a page inside another shell. Exposed as data-embedded. |
headingLevel | 'h1''h2' | The heading element. Default h1. |
States
| State | Exposed as | Behaviour |
|---|---|---|
| Static | – | The header has no state; slot content owns its states. |
| Overflowing actions | data-overflow on the More button | Set when more secondary actions exist than maxVisibleActions; without it the More button only shows below 640px. |
| Disabled action | – | A disabled action is a disabled button, and a disabled menu item (aria-disabled); a disabled href action renders as a button so it cannot be followed. |
Interaction behaviour
- Secondary action buttons call
onSelector followhref. - The More button opens a menu with every secondary action; on wide screens the items already shown as buttons are hidden from it.
- Choosing a menu item calls its
onSelect(or follows its link), closes the menu and returns focus to the More button.
Keyboard support
| Keys | Action |
|---|---|
| Tab | Moves through back link, breadcrumbs, actions and tabs in reading order. |
| Enter / Space / ArrowDown | On the More button, opens the menu. |
| ArrowUp / ArrowDown | Moves between menu items. |
| Escape | Closes the menu and returns focus to the More button. |
Accessibility
- Role
- banner landmark only when it is not inside main, article or section (native <header> rules)
- Accessible name
- The heading is the page’s main heading.
titlerenders as the h1, so screen-reader heading navigation lands on it; withembeddedit is visually hidden, not removed.- Pass a Breadcrumb in
breadcrumbsfor the navigation landmark and current-page marking. - The More button is an icon button with a required name; give it the object:
moreActionsLabel="More actions for Website refresh". - Actions keep visible text labels; icons in actions are decorative.
- The live examples on this page render with
headingLevel="h2"because the docs page has its own h1; in an app use the default h1.
Responsive behaviour
- The title block and the actions sit in a wrapping row; actions move under the title when space runs out.
- Below 640px secondary actions collapse into the More menu, so at 320px the row holds the primary action and one icon button.
- Long titles and details wrap anywhere instead of overflowing.
- The description is limited to
--mn-layout-reading-maxfor readable line length.
Theme support
- Title and muted text use Monarch typography and colour tokens, so every theme applies.
- The sticky backdrop mixes the canvas token with transparency; with
prefers-contrast: moreor without backdrop-filter support it is solid.
Do and don't
Do
- Use exactly one PageHeader per page.
- Keep a single primary action; put the rest in
secondaryActionsso they collapse on phones. - Name the More button after the page when several menus share a screen.
Don't
- Do not add another h1 elsewhere on the page.
- Do not put row- or section-level actions in the page header.
- Do not pass
onSelectcallbacks from a server component; usehrefactions there.
Related components
- Breadcrumb: The usual
breadcrumbscontent. - BackLink: The
backslot, or built for you bybackHref. - Tabs: Section navigation in the
tabsslot (TabsNav for routes). - RecordHeader: Header for one record: avatar, facts, prev/next.
- Toolbar: List tools that sit under the header.
Dependencies
| Kind | Depends on |
|---|---|
| npm packages | react |
| Monarch components | BackLink, Button, DropdownMenu |
The sticky backdrop uses --mn-z-sticky and --mn-color-bg-canvas.
Testing coverage
- Renders the title as the single h1 with actions.
- Renders banner, back link, breadcrumbs, status, details and primary action in order.
- A custom
backnode wins overbackHref. - Shows the first secondary actions as buttons and lists every action in the More menu, marking the inline ones; keyboard selection calls
onSelectand returns focus. - Keeps the More button for narrow screens when every action fits; disabled actions are disabled in both places.
- Embedded mode hides the heading visually;
headingLevelchanges the level. - Sticky mode, ref forwarding and native attributes.
- Renders on the server.
| Check | Covered |
|---|---|
| Axe in jsdom | Yes |
| Server rendering | Yes |
| Browser suites | Visual baselines: /__gallery/page-header; Cross-browser axe: /components/page-header |
Tests: packages/react/src/patterns/layout/page-header