PageHeader from @prepared911/ui-blocks is the consistent top strip for shell routes. It supplies a title, optional description, optional badges (notably ReadOnlyBadge), and a right-aligned action cluster. The header sets context for the page and keeps primary CTAs in a predictable place.
Use it to open routes that own a single subject—Admin > Settings, Incidents > List, Staff > Roster—so the operator always knows where they are and what the one or two most important actions on this page are.
- Every shell route that lands on a meaningful page. The consistent treatment aids orientation, especially when the user navigates via deep-links.
- Routes with a primary action (create, export, save). The action cluster is the canonical home for that CTA.
- Routes whose edit ability depends on permissions.
ReadOnlyBadge makes the restriction visible without a full-width banner.
- Inside
CrudPage. Use CrudPageHeader and CrudPageToolbar instead so the title and primary CTA are not duplicated—see Crud page.
- Inside modals or drawers. Modals and drawers have their own headers; don't stack another one inside.
- Pages with no actions and no description. A bare title in your layout may be enough; don't add
PageHeader for decoration.
- Full-screen committed flows. Use
TakeoverPage and its header instead.
- Title. One clear phrase; the subject of the route ("Staff", "Incident review").
- Description. Optional one-line subtitle; explain what the page offers, not what it's called.
- Read-only badge. When the user lacks edit permission, surface
ReadOnlyBadge next to the title so the state is visible without hunting. See Read-only badge on the API page for props and tooltip content.
- Actions. Right-aligned cluster; one primary plus up to two secondaries. Use an overflow menu if you need more.
- Title capitalization. Sentence case unless your product convention differs; keep it consistent across routes.
- Action verbs. "Create incident", "Export CSV", "Start shift"—verb plus object beats "Add" or "New" alone. Avoid exclamation marks.
- Description brevity. One short line or none. If you need two lines, the context probably belongs in the body, not the header.
- Read-only reason. Let the badge tooltip explain why; don't repeat the reason inline next to the title.
- Sticky on scroll. The app shell generally keeps
PageHeader pinned so actions remain reachable; honor that contract when composing.
- Loading. While the route loads, render a
Skeleton for title and description; don't suppress PageHeader entirely, or the page will shift.
- Small widths. At narrow sizes, collapse secondary actions into a menu before hiding the description. Operators rely on the title and primary action most.
Do
- Name the route, not the widget ("Staff", not "Staff list").
- Reserve the primary action slot for one clear CTA.
- Surface
ReadOnlyBadge when the page is view-only so users know up front.
- Keep the description to one line, or leave it out.
Don't
- Don't duplicate navigation crumbs inside
PageHeader; breadcrumbs live above or in the shell.
- Don't stack more than three buttons in the cluster; use an overflow menu.
- Don't vary header style per route—consistency is the point.
PageHeader renders a styled title with emphasis rather than an h1, so the surrounding route is responsible for document heading order. See the Accessibility page for heading strategy, read-only badge semantics, and keyboard paths through the action cluster.
- Crud page: list-first shell that pairs
CrudPageHeader with DataTableToolbar and a table body.
- Takeover page: full-screen committed route; brings its own header.
- Button: the primitive used in the action cluster.
- Badge: the base for
ReadOnlyBadge.