Source: https://monarchui.dev/components/error-page/

Components · Status and feedback

# ErrorPage

A whole page failed: not found, forbidden, server error, offline or maintenance.

@rookcom/monarch-react

Component

## Not found, inside the shell

## Usage examples

### Server error with support

### Offline and maintenance, without the shell

## Purpose

The page shown when a route cannot render: a 404, a 403, a server error, no connection or planned maintenance. It works on its own, without the app shell, so it can back root not-found and error routes.

### Use when

- A route does not exist or a record behind it is gone (`not-found`).
- The person may not open the page (`forbidden`).
- The page crashed or the server failed (`server`, the default).
- The app cannot reach the network (`offline`).
- The service is down on purpose (`maintenance`).

### Avoid when

- Only one region of the page failed: use ErrorState or InlineErrorRetry.
- The page loaded and is simply empty: use EmptyState.

## Design principles

- The product supplies every word: title, description, actions and the support contact. The component supplies the layout, icon and numeral.
- Never show a raw error message, stack, vendor or technology name; offer the next step instead.
- A large, faint numeral (404, 403, 500) stands in for an illustration, so no artwork is needed; connectivity and maintenance have none.
- One primary action (retry, or the product’s way home) and at most one secondary.

## API reference

```tsx
import { ErrorPage } from '@rookcom/monarch-react';
```

| Export | Kind | Rendering | Root class |
| --- | --- | --- | --- |
| `ErrorPage` | component | client | `.mn-error-page` |

The ref is forwarded to the root element (`main`, `section` or `div`). Native attributes pass through.

Actions are the product’s own elements: pass Buttons, or Button `asChild` around your router’s link.

Use `errorKindFromStatus` (exported with ErrorState) to pick a kind from an HTTP status; `maintenance` is always the product’s decision.

Generated from the TypeScript declarations of `packages/react/src/components/feedback/error-page`.

## Properties

### `ErrorPage`

A whole page that failed: not found, forbidden, server error, offline or maintenance. Works with or without the app shell. The product supplies every word, link and support contact.

| Prop | Type | Description |
| --- | --- | --- |
| `title` required | `ReactNode` | What happened, in plain words. Supplied by the product. |
| `actions` | `ReactNode` | The product's links and buttons, e.g. "Go to dashboard" (Button `asChild` around a router link). |
| `as` | `"div" \| "main" \| "section"` | The root element. Default `main` when `fullScreen` (the page has no other main landmark) and `section` otherwise (the shell already has one). |
| `brand` | `ReactNode` | The product's logo or wordmark, shown at the top when there is no app shell. |
| `children` | `ReactNode` |  |
| `className` | `string` |  |
| `description` | `ReactNode` | What the person can do next. Supplied by the product; never a raw error message. |
| `focusOnMount` | `boolean` | Moves focus to the title on mount, so a screen reader starts reading the error after a client-side navigation. Default false. |
| `fullScreen` | `boolean` | `true` (default) fills the viewport with its own background, for a root not-found or error route that renders without the app shell. `false` sits inside the shell's content area. |
| `headingLevel` | `"h2" \| "h1"` | Title element. Default `h1`. |
| `icon` | `ReactNode` | Replaces the kind's default icon. Pass `null` for none. Decorative. |
| `id` | `string` |  |
| `kind` | `ErrorPageKind` | Which failure this page reports. Sets the default icon and numeral. Default `server`. |
| `numeral` | `string \| false` | The large faint numeral behind the icon. Defaults to 404, 403 or 500 for those kinds and none for `offline` and `maintenance`. Pass `false` to hide it. Decorative: drawn by CSS and hidden from assistive technology, so say the same thing in the title. |
| `onRetry` | `(() => void)` | Renders a primary retry button before `actions`. Use for `server`, `offline` and `maintenance`. |
| `requestId` | `string` | The request id of the failed request, shown with a copy button. |
| `requestIdLabel` | `string` | Label before the request id. Default "Request ID". |
| `retrying` | `boolean` | Shows the retry button as busy while a retry is in flight. |
| `retryLabel` | `string` | Retry button text. Default "Try again". |
| `retryWhenOnline` | `boolean` | With `kind="offline"` and `onRetry`, calls `onRetry` once when the browser reports it is back online. Default true. |
| `support` | `ReactNode` | How to reach help, supplied by the product (a sentence with a link or email). |

## Variants

| Prop | Values | Description |
| --- | --- | --- |
| `kind` | `'not-found'``'forbidden'``'server'``'offline'``'maintenance'` | Default icon, icon tint and numeral (404, 403 and 500; none for offline and maintenance). Exposed as `data-kind`. |
| `fullScreen` | `'true'``'false'` | `true` (default) fills the viewport with the canvas background and its own font, for pages without the app shell; `false` sits inside the shell’s content area. `true` is exposed as `data-full-screen`. |
| `as` | `'main'``'section'``'div'` | Root element. Default `main` when full screen, `section` otherwise, so it never nests a second main landmark. |
| `numeral` | `'string'``'false'` | Overrides the kind’s numeral (e.g. "502"), or `false` to hide it. |

## States

| State | Exposed as | Behaviour |
| --- | --- | --- |
| Retrying | `aria-busy="true" on the retry button` | `retrying` shows the Button loading state and blocks repeat clicks. |
| Waiting for connection | `data-kind="offline"` | With `onRetry` and `retryWhenOnline` (default), the page retries once when the browser fires `online`. |

## Interaction behaviour

- The retry button (when `onRetry` is set) comes first and calls `onRetry`.
- Offline pages retry automatically once when the connection returns; set `retryWhenOnline={false}` to opt out.
- `focusOnMount` moves focus to the title after a client-side navigation, so screen readers start at the error.
- The request-id copy button copies the id and announces the result.

## Keyboard support

| Keys | Action |
| --- | --- |
| Tab / Shift+Tab | Moves through retry, the product’s actions, the copy button and support links. |
| Enter / Space | Activates the focused action. |

The title is focusable only by script (`tabIndex=-1`), for `focusOnMount`.

## Accessibility

Role

main (full screen) or region, labelled by the title

Accessible name

From the title, through

aria-labelledby

with a generated id, so two pages never share an id.

- The title is an h1 by default (`headingLevel="h2"` inside a shell that already has an h1).
- The numeral is drawn by CSS (`::before` from `data-numeral`) and, like the icon, `aria-hidden`; say the same thing in the title ("Page not found", not "404").
- No live region: the page is the content. Use `focusOnMount` after client-side navigation.
- Verified with axe in jsdom and in Chromium, Firefox and WebKit on /components/error-page.

## Responsive behaviour

- Content is centred in a 30rem column; the numeral scales with the viewport (`clamp`).
- Below 30rem the title steps down a size and actions stack full width.
- Full screen uses `100dvh`, so mobile browser toolbars do not cut it off.

## Theme support

- `not-found` uses the accent tile; `server` danger; `offline` warning; `forbidden` and `maintenance` the muted surface.
- Full screen sets `--mn-color-bg-canvas`, `--mn-color-text-default` and `--mn-font-sans` itself, so it looks right without the app shell.
- Colours are Monarch semantic tokens only, so light, dark and more-contrast themes apply automatically.

## Do and don't

### Do

- Write a title that says what happened without the code ("Page not found").
- Give a way forward: retry, home, or back.
- Pass your support contact through `support` and the request id when you have one.
- Use `fullScreen={false}` when the shell can still render around the error.

### Don't

- Do not show raw error messages, stack traces or vendor names.
- Do not offer retry on a 404 or 403.
- Do not render a full-screen ErrorPage inside another `main`; set `as="section"` or `fullScreen={false}`.

## Related components

- [ErrorState](https://monarchui.dev/components/error-state/): One region failed, not the whole page.
- [InlineErrorRetry](https://monarchui.dev/components/inline-error-retry/): A small section failed inside a working page.
- [EmptyState](https://monarchui.dev/components/empty-state/): The page works but has nothing to show.

## Dependencies

| Kind | Depends on |
| --- | --- |
| npm packages | `react` |
| Monarch components | [Button](https://monarchui.dev/components/button/), [CopyButton](https://monarchui.dev/components/copy-button/), [ErrorState](https://monarchui.dev/components/error-state/) |

## Testing coverage

- Full screen renders a main landmark labelled by its h1, with an aria-hidden numeral.
- Numerals default by kind and can be overridden or hidden.
- Inside a shell it renders a labelled section, never a second main; each instance has its own title id.
- Retry comes first, is keyboard operable and shows a busy state.
- Offline retries once on the `online` event, and not when `retryWhenOnline` is false.
- `focusOnMount` focuses the title; brand, support and request id render; the ref is forwarded.
- `as="div"` drops `aria-labelledby` (not allowed on a generic element).

| Check | Covered |
| --- | --- |
| Axe in jsdom | Yes |
| Server rendering | Yes |
| Browser suites | Visual baselines: /__gallery; Cross-browser axe: /components/error-page |

Tests: `packages/react/src/components/feedback/error-page`

## All example code

### Not found, inside the shell

```tsx
import { Button, ErrorPage } from '@rookcom/monarch-react';

<ErrorPage
  kind="not-found"
  fullScreen={false}
  headingLevel="h2"
  title="Page not found"
  description="The link may be broken, or the page may have moved."
  actions={
    <>
      <Button variant="primary" asChild>
        <a href="#/">Go to dashboard</a>
      </Button>
      <Button>Go back</Button>
    </>
  }
/>
```

### Server error with support

```tsx
import { ErrorPage, Link } from '@rookcom/monarch-react';

<ErrorPage
  kind="server"
  fullScreen={false}
  headingLevel="h2"
  title="Something went wrong"
  description="We couldn't open this page. Try again in a moment."
  onRetry={() => {}}
  requestId="req_5c81e0a4"
  support={
    <>
      Still stuck? <Link href="mailto:support@example.com">Contact support</Link>
    </>
  }
/>
```

### Offline and maintenance, without the shell

```tsx
import { ErrorPage } from '@rookcom/monarch-react';

<ErrorPage
  as="div"
  kind="offline"
  headingLevel="h2"
  style={{ minHeight: '24rem' }}
  brand={<strong>Acme</strong>}
  title="You're offline"
  description="Check your connection. We'll try again when you're back online."
  onRetry={() => {}}
/>
<ErrorPage
  as="div"
  kind="maintenance"
  headingLevel="h2"
  style={{ minHeight: '24rem' }}
  brand={<strong>Acme</strong>}
  title="Down for maintenance"
  description="We're making some improvements and will be back shortly."
/>
```
