---
id: "packages/active-ui/page"
title: "Page and PageHeader"
description: "The shell of a page and its header row - the title, a description, what sits beside the title, and the page's actions."
url: "/packages/active-ui/page"
locale: "en"
revision: "c4760c2a2f7c4c7c"
---

# Page and PageHeader

`Page` is the outer layer of the content of one route: it adds the page's padding and an equal
space between its blocks. `PageHeader` is the first block: the page title as `<h1>`, a description,
something beside the title such as a badge, and the actions that belong to the whole page.

**Example: A page with its header row**

Client: Runs entirely in your browser. Source: `examples/ui/page.tsx`.

```tsx
// apps/docs/examples/ui/page.tsx
'use client';

import {PlusIcon} from 'lucide-react';

import {Page} from '@workspace/active-ui/components/composed/page';
import {PageHeader} from '@workspace/active-ui/components/composed/page-header';
import {Badge} from '@workspace/active-ui/components/primitives/badge';
import {Button} from '@workspace/active-ui/components/primitives/button';

/**
 * The shell of a page with its header row: the title as `<h1>`, a badge
 * beside it, a description, and the page's actions, which move below the
 * heading on narrow screens. The page is fictitious and its buttons do
 * nothing.
 */
export default function PageExample() {
  return (
    <div className="w-full rounded-md border">
      <Page>
        <PageHeader
          title="Participants"
          description="Everyone in the scheme, active and deferred."
          suffix={<Badge variant="secondary">1,284</Badge>}
          actions={
            <>
              <Button variant="outline" size="sm">
                Export
              </Button>
              <Button size="sm">
                <PlusIcon aria-hidden />
                Add a participant
              </Button>
            </>
          }
        />
        <div className="text-muted-foreground rounded-md border border-dashed p-6 text-center text-sm">
          The page content: a table, a form, cards.
        </div>
      </Page>
    </div>
  );
}
```

## What the composition adds

- **One rhythm.** `Page` sets `space-y-6` between blocks and `px-2 py-6` around them, so the header
  owns no outer margin and every step on the page is the same.
- **A header that adapts.** Below the `sm` width the actions sit under the heading, from `sm` on to
  its right; they wrap when they do not fit.
- **Fixed sizes.** `size="default"` (`text-3xl` title) for an application with a sidebar,
  `size="compact"` (`text-2xl`) for a mobile-first environment.

`Page` sets no width: a block that needs one, such as a form, sets it on itself. The header has no
breadcrumbs, tabs or filters; tabs go below it as a sibling.

## Usage

```tsx
import {Page} from '@workspace/active-ui/components/composed/page';
import {PageHeader} from '@workspace/active-ui/components/composed/page-header';

<Page>
  <PageHeader
    title="Participants"
    description="Everyone in the scheme."
    actions={<Button size="sm">Add a participant</Button>}
  />
  …
</Page>;
```

## What your application provides

- The title and description, translated; the title is a plain string, so its accessible name stays
  predictable.
- The actions, and the decision which ones the reader may use, for example with
  [GuardedAction](/packages/active-ui/guarded-action).
- The width of the content blocks, and the outer padding of the route layout.

## Accessibility

- The title is the page's only `<h1>`. The example on this page adds a second `<h1>` to this
  documentation page; in an application there is one.
- The actions follow the heading in reading and tab order.

## API

### Page

| Prop | Type | Required | Description |
| --- | --- | --- | --- |
| `children` | `ReactNode` | yes | The complete page content, in reading order. Usually a {@link import('./page-header').PageHeader} followed by the actual content, but a {@link import('./page-header').PageHeader} is not required: a page without a visible heading is a valid page. |

**PageProps**

```ts
interface PageProps extends React.ComponentProps<'div'> {
  children: React.ReactNode;
}
```

### PageHeader

**PageHeader**

| Prop | Type | Required | Description |
| --- | --- | --- | --- |
| `title` | `string` | yes | The page title, as `<h1>`. Translated text; this component does not translate.  Deliberately a `string` and not a `ReactNode`: markup inside a heading makes the accessible name unpredictable. What belongs next to the title belongs in {@link PageHeaderProps.suffix}. |
| `actions` | `ReactNode` | no | The actions that belong to the **page as a whole**: "New X", an export button, an administration or date picker.  From `sm` up they sit to the right of the heading, below that underneath it. Multiple actions go in as separate siblings (or in a fragment); the order stays with the caller.  Deliberately **no** overflow or collapse mechanism: the actions wrap onto a next line when they do not fit. Keep the row to one or two actions. |
| `description` | `string` | no | The line below the title. Optional: a page that explains itself through its title gets no second line. When omitted, no empty element is rendered. |
| `size` | `PageHeaderSize` | no | Typographic size. Default: `"default"`. |
| `suffix` | `ReactNode` | no | Content directly next to the title, on the same line: a status badge, a help button, a small metadata marker.  Deliberately a free `ReactNode` and not a `status` or `help` prop: mapping a domain value to a tone belongs to the caller, the same boundary as with `StatusBadge` and `GuardedAction`. |

**PageHeaderSize**

```ts
type PageHeaderSize = 'default' | 'compact';
```

The typographic size of the header row.

- `"default"` — for an application with a fixed sidebar on wide screens.
Title at `text-3xl`.
- `"compact"` — for a mobile-first environment, where a `text-3xl` title
wraps over two or three lines on a phone. Title at `text-2xl`,
description one size smaller.

There is deliberately no third size and no free `titleClassName`.

**PageHeaderProps**

```ts
interface PageHeaderProps extends Omit<
  React.ComponentProps<'div'>,
  'title'
> {
  title: string;
  description?: string;
  suffix?: React.ReactNode;
  actions?: React.ReactNode;
  size?: PageHeaderSize;
}
```
