Components · Status and feedback
ErrorState
A region failed to load: what failed, why in plain words, and a retry.
Error with retry and request id
Couldn't load invoices
Check your connection and try again.
Request ID: req_7f3a91c2
import { ErrorState } from '@rookcom/monarch-react';
<ErrorState
title="Couldn't load invoices"
description="Check your connection and try again."
onRetry={() => {}}
requestId="req_7f3a91c2"
/>Usage examples
Forbidden and offline
import { Button, ErrorState } from '@rookcom/monarch-react';
<ErrorState
kind="forbidden"
size="compact"
title="You can't view this project"
description="Ask a workspace admin for access."
actions={<Button>Request access</Button>}
/>
<ErrorState
kind="offline"
size="compact"
title="You're offline"
description="We'll show the latest data once you reconnect."
onRetry={() => {}}
/>Panel error boundary
import { ErrorStateBoundary } from '@rookcom/monarch-react';
<ErrorStateBoundary
title="This panel didn't load"
description="The rest of the page still works."
>
<p>Panel content renders here until it throws.</p>
</ErrorStateBoundary>Purpose
ErrorState says what failed to load in a list, panel or card and offers a retry. ErrorStateBoundary catches a render error in one panel and shows an ErrorState instead of breaking the page.
Use when
- A list or panel failed to load (
onRetry). - The person may not see a record (
kind="forbidden"), or it no longer exists (kind="not-found"). - A request never reached the server (
kind="offline"). - One pane of a multi-pane screen should fail on its own (ErrorStateBoundary).
Avoid when
- There is simply no data: use EmptyState.
- A whole route failed or is missing: use ErrorPage.
- A small section failed inside a card that otherwise works: use InlineErrorRetry.
- Content is still loading: use LoadingState.
Design principles
- ErrorState is an EmptyState with a kind icon and a primary "Try again" button.
- Plain language only. The product maps its errors to a kind (
errorKindFromStatushelps) and writes the copy; never show a raw error message, stack or vendor name. - Retry is offered only when it can help:
forbiddenandnot-foundnever render it. - The request id is there for support conversations: monospace, selectable and copyable.
API reference
import { ErrorState, ErrorStateBoundary, errorKindFromStatus, errorKindIcon } from '@rookcom/monarch-react';| Export | Kind | Rendering | Root class |
|---|---|---|---|
ErrorState | component | server-safe | .mn-error-state |
ErrorStateBoundary | component | client | .mn-error-state |
errorKindFromStatus | utility | server-safe | – |
errorKindIcon | utility | server-safe | – |
ErrorState accepts every EmptyState prop except kind, icon and actions, which it redefines; the ref is forwarded to the root <div>.
errorKindFromStatus(status, { offline }) maps an HTTP status to a kind: 0 or offline → offline, 401/403 → forbidden, 404/410 → not-found, 5xx → server, anything else → error. maintenance is never inferred.
errorKindIcon(kind) returns the default decorative glyph for a kind, for products composing their own layout.
ErrorStateBoundary is a client component (React error boundary): fallback (node or ({ error, reset }) => node), onError, onReset and resetKeys; without fallback it renders ErrorState with the given title, description and a retry that resets it.
LoadingState moved to its own folder in 0.4 (loading-state); it is still exported from @rookcom/monarch-react with the same props and class names.
Generated from the TypeScript declarations of packages/react/src/components/feedback/error-state.
Properties
ErrorState
Something failed to load. Say what failed and offer a retry.
| Prop | Type | Description |
|---|---|---|
title required | ReactNode | |
actions | ReactNode | Extra actions next to retry. |
children | ReactNode | Extra content after the actions, such as a list of next steps. |
className | string | |
clearFiltersLabel | string | Label of the clear-filters button. Default "Clear filters". |
description | ReactNode | |
headingLevel | EmptyStateHeadingLevel | Title element. Default `h3`. Match the page outline (no skipped levels); use `p` where a heading would be wrong, such as inside a table cell. |
icon | ReactNode | Replaces the kind's default icon. |
id | string | |
kind | ErrorKind | What went wrong. Sets the default icon and whether retry is offered. Exposed as `data-kind`. |
layout | "stack" | "line" | `stack` (default) centres icon, text and actions in a column; `line` is a single left-aligned row (icon, text, actions) for a list or dashboard section that has nothing in it. |
onClearFilters | (() => void) | With `kind="filtered"`, renders a "Clear filters" button before any other actions. The product resets its own filters. |
onRetry | (() => void) | Retry handler; renders a "Try again" button (not for `forbidden` or `not-found`). |
requestId | string | The request id from the failed response, shown in monospace with a copy button so the person can quote it to support. |
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 | |
size | "inline" | "page" | "compact" | `inline` (default) sits inside a card or table; `page` fills an empty view; `compact` is a tighter block for table bodies, side panels and small cards. |
tip | ReactNode | A short hint under the actions, such as a keyboard shortcut (compose it with Kbd). Shown in small muted text. |
ErrorStateBoundary
Error boundary for one panel or region: a render error inside it shows an ErrorState with "Try again" instead of taking down the page. The rest of the page keeps working.
| Prop | Type | Description |
|---|---|---|
title required | ReactNode | |
children | ReactNode | |
description | ReactNode | |
fallback | ReactNode | ((props: ErrorStateBoundaryFallbackProps) => ReactNode) | Replaces the default fallback (an ErrorState with a retry that resets the boundary). A function receives the error and `reset`. |
headingLevel | EmptyStateHeadingLevel | Title element. Default `h3`. Match the page outline (no skipped levels); use `p` where a heading would be wrong, such as inside a table cell. |
onError | ((error: Error, info: ErrorInfo) => void) | Report the error (logging, monitoring). Called once per caught error. |
onReset | (() => void) | Called after the boundary resets, from the retry button or a `resetKeys` change. |
requestId | string | The request id from the failed response, shown in monospace with a copy button so the person can quote it to support. |
requestIdLabel | string | Label before the request id. Default "Request ID". |
resetKeys | readonly unknown[] | When any value changes (by `Object.is`) while an error is shown, the boundary resets. Pass the inputs the failed panel depends on, such as the selected record id. |
retryLabel | string | |
size | "inline" | "page" | "compact" | `inline` (default) sits inside a card or table; `page` fills an empty view; `compact` is a tighter block for table bodies, side panels and small cards. |
Variants
| Prop | Values | Description |
|---|---|---|
kind | 'error''forbidden''not-found''offline''server''maintenance' | Sets the default icon and tint and whether retry is offered. Non-default kinds are exposed as data-kind. |
size | 'inline''page''compact' | From EmptyState. compact fits a side panel or table body. |
retryLabel | 'string' | Retry button text. Default "Try again". |
States
| State | Exposed as | Behaviour |
|---|---|---|
| Error with retry | – | A primary retry button is rendered when onRetry is set and the kind allows it, before any extra actions. |
| Retrying | aria-busy="true" on the retry button | retrying shows the Button loading state: a spinner, clicks blocked, focus kept on the button. |
| Forbidden / not found | data-kind | Neutral icon tile, no retry button. |
| Offline | data-kind="offline" | Warning-tinted icon tile; retry stays available. |
Interaction behaviour
- The retry button calls
onRetry; whileretryingis true further clicks are ignored. - The request-id copy button copies the id and announces "Copied to clipboard".
- ErrorStateBoundary: "Try again" clears the error and renders the children again; a change in
resetKeysdoes the same.
Keyboard support
| Keys | Action |
|---|---|
| Tab / Shift+Tab | Moves through retry, extra actions and the copy button. |
| Enter / Space | Activates the focused button. |
Accessibility
- Role
- alert
- Accessible name
- –
- ErrorState has role="alert", so it is announced when it appears.
- The icon tile is
aria-hidden; the title carries the meaning. - While retrying, the retry button stays focusable (aria-disabled + aria-busy), so keyboard users keep their place.
- The copy button is named "Copy Request ID" (from
requestIdLabel). - Verified with axe in jsdom and in Chromium, Firefox and WebKit on /components/error-state.
Responsive behaviour
- Centred with a 28rem maximum width (from EmptyState); actions wrap.
- Long request ids break anywhere instead of overflowing at 320px.
Theme support
- The
errorandservericon tiles use the danger subtle and strong tokens;offlineuses warning;forbidden,not-foundandmaintenanceuse the muted surface. - Colours are Monarch semantic tokens only, so light, dark and more-contrast themes apply automatically.
Do and don't
Do
- Say what failed in the title.
- Pass the request id when your API returns one.
- Map statuses to kinds with
errorKindFromStatus, then pick your own copy per kind. - Wrap independent panes in ErrorStateBoundary so one failure does not blank the screen.
Don't
- Do not show raw error messages, codes or vendor names as the title.
- Do not offer retry for a permission error.
Related components
- EmptyState: No data yet.
- InlineErrorRetry: Compact retry for one section inside a card.
- ErrorPage: The whole route failed or is missing.
- LoadingState: Shown before the data arrives.
Dependencies
| Kind | Depends on |
|---|---|
| npm packages | react |
| Monarch components | Button, CopyButton, EmptyState |
Testing coverage
- ErrorState is an alert and calls
onRetry; retry renders before extra actions. - Default props keep the 0.3 markup (no
data-kind). forbiddenandnot-foundhide retry;offlinekeeps it.retryingkeeps focus on the busy button and blocks repeat activation.- The request id renders as code with a named copy button.
errorKindFromStatusmaps statuses and offline to kinds.- ErrorStateBoundary catches a render error without showing its message, calls
onError, resets on retry and onresetKeyschange, and accepts a render-prop fallback.
| Check | Covered |
|---|---|
| Axe in jsdom | Yes |
| Server rendering | Yes |
| Browser suites | Visual baselines: /__gallery; Cross-browser axe: /components/error-state |
Tests: packages/react/src/components/feedback/error-state