Page

Updated: Sep 24, 2026
Page is the standard root of an ordinary route. It renders the page header above your content, keeps that header stacked over the content area, reserves the region the display system bar occupies, and exposes the content area as a focus section so directional navigation stays inside it.
Configure the header through Page rather than rendering Header yourself: Page owns the header’s placement, stacking, and its relationship to the content below it. Use SubNavigationPager instead of Page when the route is a set of peer tabs — that component supplies its own header, so it is never combined with a page header or nested inside a Page.

Usage

import { ListItem, Page, VerticalList } from '@wearables-ui-toolkit/mrbd';

export function ActivityRoute() {
  return (
    <Page headerText="Activity" headerMetadata="Today">
      <VerticalList insetForHeader ariaLabel="Activity">
        <ListItem title="Morning brief" subtitle="Ready" onClick={openBrief} />
        <ListItem title="Team notes" subtitle="Updated" onClick={openNotes} />
      </VerticalList>
    </Page>
  );
}
When the scroll viewport itself should span the full display, turn the content inset off and let the scroller reserve the clearance instead. insetForHeader accounts for the system-bar region automatically in either mode, so the rows still start below the header:
<Page headerText="Settings" enableSystemBarInset={false}>
  <VerticalList insetForHeader ariaLabel="Settings">
    <ListItem title="Trail alerts" showSwitch checked={alerts} onCheckedChange={setAlerts} />
    <ListItem title="Weather alerts" showSwitch checked={weather} onCheckedChange={setWeather} />
  </VerticalList>
</Page>
An identity-led route puts an avatar and a status indicator in the header:
import {
  HeaderAvatarSize,
  Page,
  StatusIndicatorType,
} from '@wearables-ui-toolkit/mrbd';

<Page
  headerText="Alex Chen"
  headerMetadata="Online"
  headerShowAvatar
  headerAvatarSrc={profileImageUrl}
  headerAvatarAlt="Alex Chen"
  headerAvatarSize={HeaderAvatarSize.LARGE}
  headerStatusIndicator={StatusIndicatorType.ACTIVE}
>
  <ProfileContent />
</Page>
A route with no header at all still uses Page for the content area and inset:
<Page showHeader={false}>
  <ImmersiveContent />
</Page>

Props

The full prop type is PageProps. It extends the standard attributes of a div, so any other DOM attribute you pass is applied to the page root.

Own props

PropTypeDefaultDescription
children
ReactNode
—
Page content, rendered in the content area below the header.
showHeader
boolean
true
Whether the header renders. This is the only way to suppress it.
enableSystemBarInset
boolean
true
Whether page content is inset below the display system-bar region. The page root and the header position are unaffected.

Header props

Each of these is forwarded to the header, one for one. See Header for what the header does with them.
PropTypeDefaultDescription
headerText
string
—
Header text.
headerMaxLines
number
1
Maximum number of header text lines. The header applies 1 when this is not set.
headerMetadata
string
—
Short supporting value shown next to the header text.
headerIcon
IconSource
—
Leading header icon. Shown only when no avatar and no loading state are active.
headerShowAvatar
boolean
false
Forces the avatar into the header’s leading slot. Any avatar image or content prop also turns the avatar on.
headerAvatarSrc
string
—
Image URL for the avatar’s primary slot.
headerAvatarPrimaryContent
ReactNode
—
Content for the avatar’s primary slot. The avatar owns the slot size.
headerAvatarSecondarySrc
string
—
Image URL for the avatar’s secondary slot in the duo layout.
headerAvatarSecondaryContent
ReactNode
—
Content for the avatar’s secondary slot. The avatar owns the slot size.
headerAvatarBadgeSrc
string
—
Image URL for the avatar’s image-badge slot.
headerAvatarBadgeContent
ReactNode
—
Content for the avatar’s image-badge slot. The avatar owns the badge size.
headerAvatarAlt
string
'Avatar'
Alternative text for the avatar image. Replace the fallback with a localized string.
headerAvatarSize
HeaderAvatarSize
HeaderAvatarSize.SMALL
Avatar size.
headerStatusIndicator
StatusIndicatorType
—
Status indicator shown on the avatar.
headerStatusIndicatorIcon
IconSource
—
Glyph drawn over the status-indicator dot. Only shows when headerStatusIndicator is also set.
headerPrimaryImageShape
AvatarShape
—
Shape of the avatar’s primary image.
headerPlaceholderStyle
PlaceholderStyle
—
Placeholder treatment used while avatar images load.
headerIsLoading
boolean
false
Whether the header shows an indeterminate loader in its leading slot. Wins over both the icon and the avatar.

Notable inherited props

PropTypeDefaultDescription
role
string
'main'
Landmark role applied to the page root.
aria-label
string
Header text and metadata
Accessible name for the page root. Falls back to the derived pane title, which is absent when the header is hidden or has no text and no metadata.
className
string
''
Class added to the page root alongside the component’s own class.
style
CSSProperties
{}
Inline styles applied to the page root.

Types and enums

  • HeaderAvatarSize: SMALL, LARGE. Exported from Header.
  • StatusIndicatorType, AvatarShape, and PlaceholderStyle come from Avatar.
  • IconSource is the shared icon contract; see Icons.

Imperative handle

Page forwards a PageHandle. The ref is the handle, not the DOM node — use getElement() when you need the element itself.
MethodReturnsDescription
getHeaderHeight()
number
The rendered header height in pixels, or 0 when the header is not shown or has not been laid out yet.
getElement()
HTMLDivElement or null
The page root element, or null before mount and after unmount.
Reach for the handle only when a layout genuinely needs a measured number. Prefer insetForHeader on the child scroller, which reserves the clearance without any measurement.

Behavior

  • Header visibility. The header renders whenever showHeader is true, independent of whether any header content is set. Clearing the text leaves an empty header in place; only showHeader={false} removes it.
  • Stacking. The header and the content area are absolutely positioned siblings inside the page root. The content layer spans the full width and runs from its top inset down to the bottom edge; the header is an intrinsically sized box stacked above it, so scrolling content passes beneath the header rather than pushing it away. The header is centered horizontally and keeps a fixed offset from the top of the page.
  • Leading slot precedence. The header shows one accessory at a time: the loader when headerIsLoading is set, otherwise the avatar when headerShowAvatar or any avatar image or content prop is set, otherwise headerIcon.
  • Focus. The content area is marked as a focus section, so directional navigation resolves a neighbor inside the content before it looks outside it.
  • System-bar inset.enableSystemBarInset moves only the content area; the header keeps its fixed safe offset in both modes. A child scroller using insetForHeader reserves the correct clearance either way, so you never add top padding to compensate.
  • Clipping. The page root and the content area both clip their overflow, so a child cannot bleed past the route bounds. Anything that has to escape those bounds must be portaled out rather than absolutely positioned inside the content; see Floating portals.
  • Sizing.Page fills the space it is given and never assumes a device size. It carries no material and paints no background of its own.
  • Rendering. The component is memoized, so passing stable handler and object props keeps a busy route from re-rendering the whole page shell.

Accessibility

  • The page root is a main landmark by default; override role if a route is genuinely something else.
  • When the header is shown and has text or metadata, the page root’s accessible name is derived from them, joined with a comma, so assistive technology announces the route on arrival. Pass aria-label to override it. With no header, or with an empty header, the root has no accessible name.
  • The header itself is a top-level heading with its own accessible name; see Header.
  • Supply headerAvatarAlt whenever the header shows an avatar. The component falls back to a generic, unlocalized string.
  • Nothing in the header is focusable, so a route needs at least one real focus target in its content.
  • Use one Page as the root of an ordinary route.
  • Set header content through the header* props.
  • Keep both header text and metadata to at most two words; move detailed facts into the body. Omit metadata unless it adds high-value context.
  • Give the route one scroll container per axis inside Page, and set insetForHeader on it.
  • Pass headerIsLoading while the route’s data is in flight instead of swapping the header out for a spinner of your own.

Avoid

  • Configure the route header through Page. Rendering Header directly bypasses the page-owned placement and stacking.
  • Avoid putting a Page inside a SubNavigationPager, or a SubNavigationPager inside a Page.
  • Avoid nesting a Page inside another Page.
  • Page is layout-only: it does not handle Back, invoke a router, or read or mutate browser history. Let the host or application router own system Back and its desktop Escape fallback. An in-app back control duplicates that path.
  • Avoid adding manual top padding for the header, and avoid hardcoding a header height.
  • Avoid setting both headerIcon and an avatar expecting both to show; only one accessory renders.