Page
The main content area of a screen, with its title, description, primary action, and the sections beneath them.
A <Page> is one screen of your application. It holds the screen's title and a short description, its primary action, and the sections of content beneath them. It also applies consistent spacing and a correct heading structure, so every screen in your product feels the same.
For assistive technology, <Page> marks up the screen's main content region, so people can skip straight to it and hear the screen announced by its title.
Looking for composition guidance?
For where a page sits in the application frame and how to structure its content, see the App Frame pattern.
Usage
Reach for a page as the top-level wrapper for every screen in your application. Whatever the screen does, a list, a detail view, a settings form, it has the same shape: a header carrying the screen's title and primary action, and a handful of panels beneath it. Building every route this way is what keeps screens consistent, without anyone re-deciding the frame each time.
The mental model is one of nesting. The app shell is the application frame, the sidebar and top bar that stay put across screens. A page is the screen that lives inside that frame: it holds the screen's main heading and lets assistive technology jump straight to the content and hear it announced by its title. A panel is a single region within the page. Reach for a page when you are building the screen itself, not the frame around it or one section inside it.
Billing
Manage your plan and invoices.
Current plan
You are on the Team plan.
import { Button, Description, Page, Panel, Text, Title,} from '@marigold/components';export default () => ( <Page> <Page.Header> <Title>Billing</Title> <Description>Manage your plan and invoices.</Description> <Button variant="primary">Upgrade plan</Button> </Page.Header> <Panel> <Panel.Header> <Title>Current plan</Title> <Description>You are on the Team plan.</Description> </Panel.Header> <Panel.Content> <Text>Renews on the 1st of every month.</Text> </Panel.Content> </Panel> </Page>);Header
The page header introduces the screen. It is the first thing someone reads when the page loads: it tells the user what screen they're on and carries the screen's primary action. It has three slots: a title, an optional description, and an optional actions region on the right (covered in Actions below). Each slot has its own job.
Title
The page title names the screen. It becomes the page's main heading and the name assistive technology announces when someone lands on the page, so it carries weight beyond the label you see. Aim for a short noun phrase that names the screen, like "Billing" or "Team members".
Every page needs a name
The title doubles as the accessible name of the screen's main region. Include
a <Title>, or pass an aria-label on the
<Page> itself when the screen has no visible title.
Description
A description is a single-sentence subtitle directly under the title. Include one when the title alone doesn't tell the user what the screen is for, or what's at stake. Skip it when the title already says it. A description that restates the heading adds reading weight without adding information.
The header configures its children
<Page.Header> is the layout wrapper that arranges title, description, and actions in a grid: the title and description on the left, the actions on the right. Drop a <Title>, an optional <Description>, and any buttons (<Button>, <ButtonGroup>, <ActionMenu>, <LinkButton>) directly inside it. It works the same way as <Panel.Header>, configuring slot context so each piece picks up its heading level, id wiring, and grid position from the page. There are no sub-components of its own to reach for.
Title-only pages can skip the header
When a page has only a title, with no description or actions, drop a bare <Title> directly inside <Page> and skip the wrapper. Accessibility wires up the same way: the title becomes the page <h1> and names the <main> landmark, just as it does inside the header. As soon as the page needs a description or actions, reach for <Page.Header> so it can arrange them in the grid.
Actions
Page actions use <Button>, <LinkButton>, <ButtonGroup>, and <ActionMenu>. A page's primary action is meant to stand out, so it sits in the header as a prominent, label-sized primary button.
Wrap the actions in a <ButtonGroup> whenever the header holds more than one, whether that is two buttons or a button beside an overflow <ActionMenu>. A single action needs no group and sits in the header on its own.
Pick the element by what the action does, not by how it looks. Both forms render as the same primary button.
Acts in place (upgrade the plan, publish, save): use <Button variant="primary">, which renders a <button>.
Billing
Manage your plan and invoices.
Current plan
import { Button, Description, Page, Panel, Text, Title,} from '@marigold/components';export default () => ( <Page> <Page.Header> <Title>Billing</Title> <Description>Manage your plan and invoices.</Description> <Button variant="primary">Upgrade plan</Button> </Page.Header> <Panel> <Panel.Header> <Title>Current plan</Title> </Panel.Header> <Panel.Content> <Text>You are on the Team plan.</Text> </Panel.Content> </Panel> </Page>);Navigates (create a new entity that opens its own page, or move to another screen): use <LinkButton variant="primary">, which renders an <a>. It looks identical to the button, but because it is a real link it behaves like one (open in a new tab, copy the address, follow with the keyboard).
Upcoming
import { Description, LinkButton, Page, Panel, Text, Title,} from '@marigold/components';export default () => ( <Page> <Page.Header> <Title>Events</Title> <Description>Create and manage your events.</Description> <LinkButton variant="primary" href="/events/new"> Create event </LinkButton> </Page.Header> <Panel> <Panel.Header> <Title>Upcoming</Title> </Panel.Header> <Panel.Content> <Text>No events scheduled yet.</Text> </Panel.Content> </Panel> </Page>);A page has one prominent primary action, and it belongs to the page. Put it in the header for an action that operates on the whole screen, such as creating a new entity. For a form, the primary action is instead the submit button at the end of the form. A <Panel> is a surface within the page, so its header carries actions scoped to that surface rather than the page's primary call to action.
Width
<Page> does not impose a max-width. Content fills the main area, and width is decided per surface rather than per page. Wrap form-style content in <Panel size="form"> to constrain it to a comfortable reading measure, and leave data-dense surfaces (tables, dashboards) full-width.
// A settings page: each surface constrained to a readable measure
<Page>
<Page.Header>
<Title>Account preferences</Title>
</Page.Header>
<Panel size="form">{/* … */}</Panel>
<Panel size="form">{/* … */}</Panel>
</Page>This is a deliberate departure from systems that expose a page-level fullWidth / narrowWidth switch. A real page often mixes a narrow form with a wide table, and constraining per surface keeps each readable without a single page-level width fighting the exceptions.
Status and metadata
The header has room for a title, a description, and the page's primary action, and nothing else. Object status (a <Badge> such as "Published" or "Suspended") and at-a-glance metadata (counts, dates, owners) do not belong in it. Crowding the header with them buries the one action a user came there for, and there is no slot to hold them anyway.
Show them in the page body instead. On a detail or record screen, the established shape is a primary column with the main content beside a narrower secondary <Panel>, usually titled "Overview", that carries the status and the key facts. The detail layout in the App Frame pattern walks through this primary/secondary split.
Summer Festival
Event details and ticketing.
About
Overview
import { Badge, Columns, Description, Inline, Page, Panel, Stack, Text, Title,} from '@marigold/components';export default () => ( <Page> <Page.Header> <Title>Summer Festival</Title> <Description>Event details and ticketing.</Description> </Page.Header> <Columns columns={[2, 1]} space="group" collapseAt="40rem"> {/* Primary column: the page's main content */} <Panel> <Panel.Header> <Title>About</Title> </Panel.Header> <Panel.Content> <Text> A weekend of live music across three stages, with food stalls and hands-on workshops. </Text> </Panel.Content> </Panel> {/* Secondary column: status and at-a-glance metadata, not the header */} <Panel> <Panel.Header> <Title>Overview</Title> </Panel.Header> <Panel.Content> <Stack space="regular"> <Stack space="tight"> <Text variant="muted" fontSize="xs"> Status </Text> <Inline> <Badge variant="success">Published</Badge> </Inline> </Stack> <Stack space="tight"> <Text variant="muted" fontSize="xs"> Tickets sold </Text> <Text weight="semibold">1,284</Text> </Stack> </Stack> </Panel.Content> </Panel> </Columns> </Page>);Spacing
Most screens never need to override the spacing. The point of <Page> is that its padding and vertical rhythm come tuned, so every screen in your product shares one frame without anyone setting spacing by hand. Out of the box that is square-relaxed padding around the content and a group gap between the header and the sections beneath it.
Reach for an override only when a screen has a reason the defaults cannot anticipate:
- Tighter padding with
p(orpxandpyfor one axis) when the page is embedded in a surface that already supplies its own outer spacing. Set one or the other. Passingptogether withpxorpyis a type error, since they would fight over the same edges. - A different rhythm with
spacewhen a sparse screen wants more air between the header and a lone panel, or when a dense screen reads better pulled tight.
Content
Put <Panel>s directly inside <Page> for the common case. Reach for the optional <Page.Content> wrapper only when the rhythm between sections should differ from the header-to-content gap, for instance a header set well apart from a group of panels that themselves sit close together. <Page>'s space then controls the header-to-content gap, and <Page.Content space> the gap between sections.
<Page space="section">
<Page.Header>…</Page.Header>
<Page.Content space="regular">
<Panel>…</Panel>
<Panel>…</Panel>
</Page.Content>
</Page>Accessibility
- Landmark:
<Page>renders a<main>landmark. When a<Title>is present in<Page.Header>, the<main>is labelled by it viaaria-labelledby, so screen-reader landmark navigation announces the page by its title. - No title: If a page has no
<Title>, pass anaria-labelto<Page>so the<main>still has an accessible name. - Heading outline:
<Page>sets the base heading level to1, so the page title renders as the<h1>and each<Panel>below it as an<h2>(Panel collapsibles as<h3>). The outline a screen-reader user skims to jump between sections falls out of these defaults, with no heading levels to set by hand.
Props
Page
Prop
Type
Accessibility props (54)
Prop
Type
DOM event handlers (164)
Prop
Type
Page.Header
Prop
Type
Page.Content
Prop
Type
Related components
AppShell: The grid frame that positions the page, sidebar, and header.
Panel: The titled surfaces that make up a page's content, sharing the same header composition model.
ErrorState: The fallback UI when a page fails to render. See the error boundaries pattern for the full convention.