# Insurance

> A household's insurance: the policies, what is and is not covered, renewals and notice periods, and the open claim beside them.

Pattern · Pro kit · [Live page](https://ui.twentyfirst.media/pro/patterns/InsuranceScreen/)

A household's insurance: the policies, what is and is not covered, renewals and notice periods,
and the open claim beside them.

## Code

```tsx
import type * as React from "react";
import {
  AppShell,
  Button,
  ClaimTimeline,
  CoverageMatrix,
  DeadlineList,
  PageHeader,
  Panel,
  PolicyCard,
  Stagger,
} from "@twentyfirstmedia/pro";
import type { SidebarGroup } from "@twentyfirstmedia/pro";

const NAV: SidebarGroup[] = [
  {
    items: [
      { id: "today", label: "Today", icon: "sun" },
      { id: "assistant", label: "Assistant", icon: "sparkle", badge: "dot" },
    ],
  },
  {
    label: "Life",
    items: [
      { id: "health", label: "Health", icon: "heart" },
      { id: "habits", label: "Habits & journal", icon: "check" },
      { id: "money", label: "Money", icon: "wallet" },
      { id: "insurance", label: "Insurance", icon: "shield" },
      { id: "documents", label: "Documents", icon: "folder", count: 2 },
    ],
  },
];

/** Insurance for a household: policies, what is and is not covered, an open claim, renewals. */
export default function Preview() {
  return (
    <AppShell
      navigation="sidebar"
      brand="Life"
      sidebarGroups={NAV}
      active="insurance"
      user={{ name: "Mara Ellis", email: "mara@halo.app" }}
      dock={false}
      side={
        <Panel side title="Open claim">
          <ClaimTimeline
            title="Water damage in the kitchen"
            meta="Claim 2026-4418 · Nordhafen"
            amount={2340}
            steps={[
              { label: "Reported", date: new Date(2026, 9, 28), state: "done" },
              { label: "Assessor visit", date: new Date(2026, 10, 4), state: "done" },
              { label: "Decision", state: "current", note: "Usually within 10 working days" },
              { label: "Payout", state: "todo" },
            ]}
          />
        </Panel>
      }
    >
      <div className="tf-in">
        <PageHeader
          title="Insurance"
          subtitle="4 policies · €118 a month"
          actions={[
            <Button key="n" variant="primary" icon="plus">
              Add policy
            </Button>,
          ]}
        />
      </div>
      <Stagger
        className="tf-grid"
        style={
          {
            "--cols": "repeat(2, minmax(0, 1fr))",
            "--cols-sm": "1fr",
            gap: 16,
          } as React.CSSProperties
        }
      >
        <PolicyCard
          type="Household contents"
          insurer="Nordhafen Insurance"
          icon="home"
          coverage={65000}
          premium={{ amount: 9.8, period: "month" }}
          deductible={150}
          renews={new Date(2027, 2, 1)}
        />
        <PolicyCard
          type="Car (comprehensive)"
          insurer="Alpenland Direct"
          icon="key"
          tint="butter"
          coverage="Unlimited liability"
          premium={{ amount: 412, period: "year" }}
          deductible={300}
          status="expiring"
          renews={new Date(2026, 11, 31)}
        />
      </Stagger>
      <Panel title="Are we covered?" subtitle="1 gap, 2 partly">
        <CoverageMatrix
          rows={[
            {
              id: "liab",
              risk: "Damage to others",
              status: "covered",
              policy: "Personal liability",
              note: "Up to €10 m",
            },
            {
              id: "bike",
              risk: "Bike theft",
              status: "partial",
              policy: "Household contents",
              note: "Only up to €1,000",
            },
            {
              id: "work",
              risk: "Unable to work",
              status: "none",
              note: "The biggest gap for one income",
            },
            {
              id: "legal",
              risk: "Legal costs",
              status: "partial",
              policy: "Legal protection",
              note: "Traffic only",
            },
          ]}
        />
      </Panel>
      <Panel title="Renewals and notice periods">
        <DeadlineList
          today={new Date(2026, 10, 13)}
          items={[
            {
              id: "car",
              title: "Car insurance: switch by",
              kind: "Notice period",
              date: new Date(2026, 10, 30),
            },
            {
              id: "car2",
              title: "Car insurance renews",
              kind: "Renewal",
              date: new Date(2026, 11, 31),
              amount: 412,
            },
            {
              id: "home",
              title: "Household contents renews",
              kind: "Renewal",
              date: new Date(2027, 2, 1),
              amount: 117.6,
            },
          ]}
        />
      </Panel>
    </AppShell>
  );
}
```

## Props

### AppShell

| Prop | Type | Notes |
| --- | --- | --- |
| `active?` | `string` |  |
| `brand?` | `ReactNode` | The product name, or a node (a logo mark with the name; collapsed sidebars show it whole). |
| `tag?` | `string` |  |
| `nav?` | `NavItem[]` |  |
| `user?` | `{ name: string; email?: string }` |  |
| `onNavigate?` | `(label: string) => void` |  |
| `topExtra?` | `ReactNode` |  |
| `sheetStyle?` | `CSSProperties` |  |
| `contentKey?` | `string \| number` |  |
| `side?` | `ReactNode` |  |
| `dock?` | `boolean` |  |
| `dockItems?` | `DockProps["items"]` |  |
| `railTop?` | `RailItem[]` |  |
| `railBottom?` | `RailItem[]` |  |
| `navigation?` | `"top" \| "sidebar"` | `top` (default): navigation in the top bar, tools in the side rail. `sidebar`: a labelled sidebar on the frame, with groups, nesting and a drawer on phones. |
| `sidebarGroups?` | `SidebarGroup[]` | With `navigation="sidebar"`: groups instead of the flat `nav` list. |
| `sidebar?` | `Partial<Omit<SidebarProps, "variant" \| "groups">>` | With `navigation="sidebar"`: more sidebar props (collapsed, storageKey, shortcut, linkAs …). |
| `workspaces?` | `WorkspaceSwitcherProps` | With `navigation="sidebar"`: the workspace switcher at the top instead of the brand. |
| `userMenu?` | `(MenuItem \| "-")[]` | The account menu's entries (with `navigation="sidebar"` at the foot of the sidebar). |
| `children?` | `ReactNode` |  |
| `className?` | `string` | Extra class on the root element. |
| `style?` | `CSSProperties` | Inline style on the root element. |

### PolicyCard

| Prop | Type | Notes |
| --- | --- | --- |
| `surface?` | `Surface` | `"glass"`: translucent glass over a backdrop (`Ambient`, a photo, a map); `"solid"` opts out inside a glass `UIProvider`. |
| `type` | `string` | The kind of insurance: "Household contents", "Liability" … |
| `insurer` | `string` | Required |
| `icon?` | `IconName` |  |
| `tint?` | `Tint` |  |
| `policyNumber?` | `string` |  |
| `maskNumber?` | `boolean` | Show only the last four characters of the policy number. |
| `coverage?` | `number \| ReactNode` | Sum insured: a number (money) or text ("unlimited"). |
| `premium?` | `{ amount: number; period: "month" \| "quarter" \| "year" }` |  |
| `deductible?` | `number` |  |
| `renews?` | `Date \| string \| number` |  |
| `status?` | `"active" \| "pending" \| "expiring" \| "lapsed"` |  |
| `currency?` | `string` |  |
| `actions?` | `ReactNode` | Buttons at the foot (documents, report a claim …). |
| `className?` | `string` | Extra class on the root element. |
| `style?` | `CSSProperties` | Inline style on the root element. |

### CoverageMatrix

| Prop | Type | Notes |
| --- | --- | --- |
| `rows` | `CoverageRow[]` | Required |
| `label?` | `string` | Accessible name and caption (default "Coverage"). |
| `className?` | `string` | Extra class on the root element. |
| `style?` | `CSSProperties` | Inline style on the root element. |

### ClaimTimeline

| Prop | Type | Notes |
| --- | --- | --- |
| `title` | `string` | Required |
| `meta?` | `ReactNode` | Reference and insurer under the title. |
| `amount?` | `number` |  |
| `currency?` | `string` |  |
| `steps` | `ClaimStep[]` | Required |
| `className?` | `string` | Extra class on the root element. |
| `style?` | `CSSProperties` | Inline style on the root element. |

### DeadlineList

| Prop | Type | Notes |
| --- | --- | --- |
| `items` | `DeadlineItem[]` | Required |
| `today?` | `Date \| string \| number` | The day to count from (default: now). Set it for server rendering and stable output. |
| `soon?` | `number` | Days ahead that count as soon (default 14). |
| `currency?` | `string` |  |
| `onDone?` | `(id: string) => void` |  |
| `label?` | `string` | Accessible name (default "Deadlines"). |
| `className?` | `string` | Extra class on the root element. |
| `style?` | `CSSProperties` | Inline style on the root element. |

### PageHeader

| Prop | Type | Notes |
| --- | --- | --- |
| `crumbs?` | `(string \| Crumb)[]` | A breadcrumb above the title (see `Breadcrumb`): labels, or items with `href`/`onClick`. |
| `crumbLinkAs?` | `ElementType` | The element crumb links render as, e.g. Next.js `Link`. |
| `title` | `string` | Required |
| `subtitle?` | `string` |  |
| `actions?` | `ReactNode[]` |  |
| `className?` | `string` | Extra class on the root element. |
| `style?` | `CSSProperties` | Inline style on the root element. |

### Panel

| Prop | Type | Notes |
| --- | --- | --- |
| `side?` | `boolean` |  |
| `title?` | `string` |  |
| `subtitle?` | `string` |  |
| `action?` | `ReactNode` |  |
| `children?` | `ReactNode` |  |
| `surface?` | `Surface` | `"glass"`: translucent glass over what is behind (maps, 3D, video, photos); `"solid"` opts out inside a glass `UIProvider`. |
| `className?` | `string` | Extra class on the root element. |
| `style?` | `CSSProperties` | Inline style on the root element. |

### Stagger

| Prop | Type | Notes |
| --- | --- | --- |
| `as?` | `string` |  |
| `className?` | `string` |  |
| `style?` | `CSSProperties` |  |
| `delay?` | `number` | Extra delay before the first child, in ms. |
| `reveal?` | `boolean` | Below the fold, wait until the group scrolls into view (default true). |
| `children?` | `ReactNode` |  |

### Button

| Prop | Type | Notes |
| --- | --- | --- |
| `variant?` | `"primary" \| "secondary" \| "soft" \| "ghost" \| "danger" \| "danger-solid"` |  |
| `size?` | `"md" \| "sm"` |  |
| `icon?` | `IconName` |  |
| `iconRight?` | `IconName` |  |
| `disabled?` | `boolean` |  |
| `type?` | `"button" \| "submit" \| "reset"` |  |
| `onClick?` | `(e: MouseEvent<HTMLButtonElement>) => void` |  |
| `children?` | `ReactNode` |  |
| `asChild?` | `boolean` | Render the single child element (a link, a router Link) with the button's look instead of a <button>. |
| `id?` | `string` |  |
| `name?` | `string` | Form field name and value, sent with the form when this button submits it. |
| `value?` | `string` |  |
| `form?` | `string` | The id of the form this button belongs to, when it sits outside it. |
| `title?` | `string` |  |
| `tabIndex?` | `number` |  |
| `autoFocus?` | `boolean` |  |
| `aria-label?` | `string` |  |
| `aria-labelledby?` | `string` |  |
| `aria-describedby?` | `string` |  |
| `aria-expanded?` | `boolean \| "true" \| "false"` | For disclosure and menu triggers: whether what the button controls is open. |
| `aria-controls?` | `string` |  |
| `aria-haspopup?` | `boolean \| "true" \| "false" \| "menu" \| "listbox" \| "tree" \| "grid" \| "dialog"` |  |
| `aria-pressed?` | `boolean \| "true" \| "false" \| "mixed"` | For toggle buttons. |
| `aria-current?` | `boolean \| "true" \| "false" \| "page" \| "step" \| "location" \| "date" \| "time"` |  |
| `className?` | `string` | Extra class on the root element. |
| `style?` | `CSSProperties` | Inline style on the root element. |
