Page Header
block / layout
Page title, description, breadcrumbs, meta, actions and tabs in one header, with a sticky variant that condenses on scroll.
- type
- block
- files
- 1
- registry deps
- badge, button, dropdown-menu, skeleton, @automindz/breadcrumbs, @automindz/render-link
- npm deps
- lucide-react
installs
- components/automindz/page-header.tsx
- Breadcrumbs, actions and tabs arrive as data, not markup
- Sticky variant condenses on scroll with no layout shift
- Actions collapse into an overflow menu, never a second row
- Awaited actions: pending in place, failure with a retry
Breadcrumbs, actions and tabs arrive as data, not slots — the header can only move an action into an overflow menu if it knows what the action is. meta is the one node slot. sticky condenses on scroll and reserves the height it loses, so the page never shifts; it finds its own scroll container; set stickyOffset for anything pinned above it (48 under @automindz/dashboard-shell). Pass activeTab matching each tab's id ?? href ?? label. onSelect on an action is awaited. renderLink swaps in next/link.
Variations
one state per section
- 01Every slot, from data
- 02With page tabs
- 03Sticky, condensing on scroll
- 04Narrow: actions collapse
- 05Awaited actions
- 06Loading
Every slot, from data
Trail, title, description, meta and actions in one header. breadcrumbs is a Crumb[] handed to @automindz/breadcrumbs rather than a markup slot, and actions is an array — the header can only move an action into a menu if it knows what the action is. meta is the one node slot; the header owns its spacing and its type size so two pages cannot disagree.
Billing & invoices
Plan, payment method and every invoice raised against this workspace. Changes take effect on the next billing cycle.
With page tabs
Tabs are links in a <nav>, not a tablist: a tablist promises panels in this document, and these change the page. The active one marks itself aria-current="page". Tax documents is blocked with a reason and keeps its place — hiding a tab just makes people ask where it went. The row carries the header's bottom rule, so the active underline sits on it.
Billing & invoices
Plan, payment method and every invoice raised against this workspace. Changes take effect on the next billing cycle.
Sticky, condensing on scroll
Scroll inside the frame. Past the top the title shrinks, the trail, description and meta collapse to nothing and go inert, and the actions and tabs stay reachable. Watch the blocks below: the header grows its own bottom margin by whatever its box loses, so nothing slides while it condenses. The header finds its own scroll container, so this needs no configuration in a preview or in an app shell — only stickyOffset when something is pinned above it. prefers-reduced-motion drops the transition and the condense snaps.
Billing & invoices
Plan, payment method and every invoice raised against this workspace. Changes take effect on the next billing cycle.
Narrow: actions collapse
Four actions in a 24rem column. The primary stays visible and the rest become an overflow menu rather than wrapping to a second row — measured against the header's own width, so a header in a narrow content column collapses on a wide screen too. Sync now is blocked but focusable and explains itself; Delete workspace is really disabled.
Billing & invoices
Plan, payment method and every invoice raised against this workspace. Changes take effect on the next billing cycle.
Awaited actions
onSelect is awaited, so the header owns the state. Press Export CSV: it takes the spinner and aria-busy, its siblings recede rather than just going flat, and the rejection lands in place under role="alert" while the button becomes Try again. The alert sits outside the collapse — a failure the condense hides is a failure nobody reads.
Billing & invoices
Plan, payment method and every invoice raised against this workspace. Changes take effect on the next billing cycle.
Loading
The data-driven text becomes skeletons inside the real elements — same paddings, same line boxes — so the header keeps its full structure and its height to within a line of text. One description line and one meta row are reserved, so a description that wraps to two lines settles by a line rather than by the whole block. The config-driven slots keep rendering: the trail, the tabs and the action row are all known before the data is.
