# Page

The column, the title block and the sections that give each page the same structure.

```tsx
import { Badge, Button, Card, CardContent, PageContainer, PageHeader, PageSection } from 'ferry-ui'
import { UserPlus } from 'lucide-react'

const MEMBERS = [
  { name: 'Maya Chen', email: 'maya@example.com', role: 'Owner' },
  { name: 'Jonas Weber', email: 'jonas@example.com', role: 'Admin' },
  { name: 'Priya Patel', email: 'priya@example.com', role: 'Member' },
]

export default function PageHero() {
  return (
    <div className="rounded-lg border">
      <PageContainer size="narrow">
        <PageHeader
          title="Members"
          description="People with access to this workspace."
          actions={
            <Button variant="primary" icon={<UserPlus />}>
              Invite member
            </Button>
          }
        />
        <PageSection title="Active members" description="They can sign in today.">
          <Card className="divide-y">
            {MEMBERS.map((member) => (
              <CardContent key={member.email} className="flex items-center justify-between gap-4">
                <div className="flex min-w-0 flex-col">
                  <span className="truncate text-sm text-foreground">{member.name}</span>
                  <span className="truncate text-[13px] text-foreground-light">{member.email}</span>
                </div>
                <Badge>{member.role}</Badge>
              </CardContent>
            ))}
          </Card>
        </PageSection>
        <PageSection title="Pending invitations" description="An invitation stays valid for 7 days.">
          <Card>
            <CardContent className="text-[13px] text-foreground-light">No invitation is pending.</CardContent>
          </Card>
        </PageSection>
      </PageContainer>
    </div>
  )
}
```

## Usage guidelines

- **One container for each page.** Put `PageContainer` in the page region of [App Shell](/docs/components/app-shell). Do not nest containers.
- **One header for each page.** `PageHeader` is the first child of the container. It holds the only `<h1>` of the page.
- **One section for each topic.** `PageSection` divides the page into topics, such as "General" and "Billing". It does not group the rows of a card.
- **One level up is a back link.** `PageBackLink` points to the parent page. For a deeper hierarchy, use a [Breadcrumb](/docs/components/breadcrumb).

## Anatomy

Import the parts and put them together.

```tsx title="Anatomy"

<PageContainer>
  <PageHeader title="" eyebrow={<PageBackLink href="" />} />
  <PageSection title="" />
  <PageSection title="" />
</PageContainer>
```

| Part | Element | Role |
| --- | --- | --- |
| `PageContainer` | `div` | The centered column. It sets the maximum width and the padding. |
| `PageHeader` | `header` | The title block. It holds the `h1` of the page. |
| `PageBackLink` | `a` | The link to the parent page. It goes in `eyebrow`. |
| `PageSection` | `section` | One topic of the page. Its title is an `h2`. |

## Examples

### Actions

Give the buttons of the page to `actions`. Put the secondary buttons first and the primary button last.

```tsx
import { Button, PageHeader } from 'ferry-ui'
import { Download, Plus } from 'lucide-react'

export default function PageHeaderActions() {
  return (
    <PageHeader
      title="Invoices"
      description="Each invoice of your customers, newest first."
      actions={
        <>
          <Button icon={<Download />}>Export</Button>
          <Button variant="primary" icon={<Plus />}>
            New invoice
          </Button>
        </>
      }
    />
  )
}
```

### Badges

`badges` shows small elements after the title. Use it for a tag or for the status of a record.

```tsx
import { Badge, PageHeader } from 'ferry-ui'

export default function PageHeaderBadges() {
  return (
    <PageHeader
      title="Usage reports"
      description="The API calls of each project, day by day."
      badges={<Badge variant="info">Beta</Badge>}
    />
  )
}
```

### Back link

On a detail page or a create page, put a `PageBackLink` in `eyebrow`. Write only the name of the parent page. The component adds the arrow.

```tsx
import type * as React from 'react'
import { PageBackLink, PageHeader } from 'ferry-ui'

// The demo stays on this page. In an app, give the path to `href` and remove `onClick`.
const stay = (event: React.MouseEvent) => event.preventDefault()

export default function PageBackLinkDemo() {
  return (
    <PageHeader
      eyebrow={
        <PageBackLink href="#" onClick={stay}>
          Projects
        </PageBackLink>
      }
      title="Create a project"
      description="Give the project a name. You can change it later in the settings."
    />
  )
}
```

### Large title

Set `size="lg"` on the home page of one record. The title and the description become larger.

```tsx
import type * as React from 'react'
import { Button, PageBackLink, PageHeader, StatusBadge } from 'ferry-ui'
import { Download } from 'lucide-react'

// The demo stays on this page. In an app, give the path to `href` and remove `onClick`.
const stay = (event: React.MouseEvent) => event.preventDefault()

export default function PageHeaderLarge() {
  return (
    <PageHeader
      size="lg"
      eyebrow={
        <PageBackLink href="#" onClick={stay}>
          Customers
        </PageBackLink>
      }
      title="Acme"
      description="Customer since March 2024. 42 seats."
      badges={<StatusBadge tone="success" label="Active" size="sm" />}
      actions={<Button icon={<Download />}>Export</Button>}
    />
  )
}
```

### Content below the title

The children of `PageHeader` show below the title row. Use them for [Tabs](/docs/components/tabs) or a [Callout](/docs/components/callout).

```tsx
import { Button, PageHeader, Tabs, TabsList, TabsTrigger } from 'ferry-ui'
import { UserPlus } from 'lucide-react'

export default function PageHeaderTabs() {
  return (
    <PageHeader
      title="Team"
      description="The people who can open this workspace."
      actions={
        <Button variant="primary" icon={<UserPlus />}>
          Invite member
        </Button>
      }
    >
      <Tabs defaultValue="members">
        <TabsList aria-label="Team sections">
          <TabsTrigger value="members">Members</TabsTrigger>
          <TabsTrigger value="invitations">Invitations</TabsTrigger>
          <TabsTrigger value="roles">Roles</TabsTrigger>
        </TabsList>
      </Tabs>
    </PageHeader>
  )
}
```

### Sections

`PageSection` takes a `title`, a `description` and `actions`. Pass an `id` to link to the section. The space between two sections is 40px.

```tsx
import { Badge, Button, Card, CardContent, PageSection } from 'ferry-ui'
import { Plus } from 'lucide-react'

export default function PageSections() {
  return (
    <div>
      <PageSection
        id="payment-methods"
        title="Payment methods"
        description="The default card pays the invoice of each month."
        actions={
          <Button size="tiny" icon={<Plus />}>
            Add card
          </Button>
        }
      >
        <Card>
          <CardContent className="flex items-center justify-between gap-4 text-sm">
            <span className="text-foreground">Card with the last digits 4242</span>
            <Badge>Default</Badge>
          </CardContent>
        </Card>
      </PageSection>
      <PageSection id="billing-address" title="Billing address">
        <Card>
          <CardContent className="text-sm text-foreground">Acme, 12 Harbor Street, Portland</CardContent>
        </Card>
      </PageSection>
    </div>
  )
}
```

### Container width

The `size` prop of `PageContainer` sets the maximum width of the column.

```tsx
<PageContainer size="narrow">
  <PageHeader title="Project settings" />
</PageContainer>
```

| Size | Content width | Use |
| --- | --- | --- |
| `narrow` | About 800px | Settings, forms, detail pages. |
| `default` | About 1200px | Lists, overviews, dashboards. |
| `full` | No maximum | Logs, wide tables, editors. |

## Accessibility

- The title of `PageHeader` is the `<h1>` of the page. Do not add a second `<h1>`.
- The title of a `PageSection` is an `<h2>`. It also gives the section its accessible name.
- The arrow of `PageBackLink` is decorative. The name of the parent page is the text of the link.

## API reference

Each part also accepts the attributes of its element.

### PageContainer

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `size` | `"default" \| "full" \| "narrow"` | `default` | Max content width (default `default`). See `PageContainerSize`. |

### PageHeader

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `title` (required) | `ReactNode` |  | Page title, rendered as the page's `<h1>` (truncated on one line). |
| `description` | `ReactNode` |  | One-sentence subtitle under the title (lighter foreground). |
| `badges` | `ReactNode` |  | Small badges next to the title (a `Beta` tag, a status badge). |
| `actions` | `ReactNode` |  | Right-aligned toolbar: secondary buttons first, the page's primary action last. |
| `eyebrow` | `ReactNode` |  | Small line above the title (13px, lighter foreground): a `PageBackLink` to the parent list on a detail or create page, or a `Breadcrumb` for deeper hierarchies. |
| `size` | `"md" \| "lg"` | `md` | `md` (default): 24 → 26px title for list, overview and settings pages. `lg`: 26 → 32px title (and 16px description) for the home page of a single record. |
| `children` | `ReactNode` |  | Extra content under the title row (tabs, a stat strip, a callout). |

### PageBackLink

`PageBackLink` uses the link component of [Link Provider](/docs/utilities/link-provider).

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `href` (required) | `string` |  | Destination: the parent page (usually the list this record belongs to). |
| `children` (required) | `ReactNode` |  | Name of the parent page, without an arrow or "Back to" (`Customers`, `All invoices`). |
| `linkComponent` | `LinkComponent` |  | Link component for this link only, overriding the one from the nearest `LinkProvider` (a plain `<a>` by default). Usually you set a `LinkProvider` once at the app root instead. |

### PageSection

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `title` | `ReactNode` |  | Section heading, rendered as an `<h2>` (18 → 20px medium) that also labels the section. |
| `description` | `ReactNode` |  | One-sentence explanation under the heading. |
| `actions` | `ReactNode` |  | Compact controls aligned with the heading on the right (a "View all" link, an "Add" button). |
