Components · Status and feedback
ErrorPage
A whole page failed: not found, forbidden, server error, offline or maintenance.
Not found, inside the shell
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>
</>
}
/>Usage examples
Server error with support
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:[email protected]">Contact support</Link>
</>
}
/>Offline and maintenance, without the shell
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."
/>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
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
onRetryis set) comes first and callsonRetry. - Offline pages retry automatically once when the connection returns; set
retryWhenOnline={false}to opt out. focusOnMountmoves 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-labelledbywith 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 (
::beforefromdata-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
focusOnMountafter 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-founduses the accent tile;serverdanger;offlinewarning;forbiddenandmaintenancethe muted surface.- Full screen sets
--mn-color-bg-canvas,--mn-color-text-defaultand--mn-font-sansitself, 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
supportand 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; setas="section"orfullScreen={false}.
Related components
- ErrorState: One region failed, not the whole page.
- InlineErrorRetry: A small section failed inside a working page.
- EmptyState: The page works but has nothing to show.
Dependencies
| Kind | Depends on |
|---|---|
| npm packages | react |
| Monarch components | Button, CopyButton, ErrorState |
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
onlineevent, and not whenretryWhenOnlineis false. focusOnMountfocuses the title; brand, support and request id render; the ref is forwarded.as="div"dropsaria-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