/// Feedback
ErrorState
A centered failure panel with a danger accent rule, optional debugId tag and a retry affordance.
import { ErrorState } from "@protocore/pds";Basics
A title and detail under an ERROR eyebrow. Both default sensibly, so the smallest useful ErrorState is nearly propless.
The indexer returned no response. This is usually transient.
Retry and debugId
Pass onRetry to render a retry button, and a debugId to surface the request/trace id as a mono metadata tag — the string support will ask for.
Block height diverged from the canonical chain.
debugId req_8f21c4a0The five-state doctrine
ErrorState is state 3 of the five — *loading, empty, error, pending, data*. It means the request failed. Two rules keep it honest:
1. Always offer a way forward. Wire onRetry for anything transient; a dead end with no retry is the most common error-handling drift. 2. Always carry a `debugId`. The request id, trace id, or correlation id turns "it's broken" into a searchable support ticket.
Don't confuse this with empty — a fetch that succeeds with zero rows is an EmptyState, and dressing a failure as "No data" hides the problem from the user *and* your logs.
ErrorState vs. Banner vs. Toast
Pick by scope and blast radius:
- ErrorState — the whole panel/view has no content to show because loading it failed. It *replaces* the content.
- [Banner](/feedback/banner) — the surface still works, but there's a persistent, page-level problem to flag (degraded region, expired token).
- [Toast](/feedback/toast) — a transient failure of a discrete action ("Couldn't save") that doesn't block the rest of the view.
Usage
Do
- Wire onRetry for anything that might succeed on a second attempt.
- Always surface a debugId so failures are traceable.
- Write a plain-language title; put the technical cause in detail.
- Use it when loading the surface failed and there's nothing to show.
Don't
- Leave a failed surface with no retry and no id — a silent dead end.
- Dump a raw stack trace or HTTP status as the title.
- Use ErrorState for a transient action failure — that's a Toast.
- Show "No data" (EmptyState) when the request actually errored.
Accessibility
| Keys | Action |
|---|---|
| Tab | Moves focus to the retry button when one is rendered. |
| Enter / Space | Activates the focused retry button. |
- The root is `role="alert"`, so its content is announced assertively when it appears.
- The `icon` slot is decorative (`aria-hidden`); the message lives in the title and detail.
- The `debugId` renders as visible mono text, so it can be read aloud and copied — not hidden in an attribute.
- The retry control is a real `<button>` with full keyboard and focus-ring support.
ErrorState props
| Prop | Type | Default | Description |
|---|---|---|---|
className | string | — | |
debugId | string | — | Machine debug identifier, rendered as a mono metadata tag. |
detail | ReactNode | — | Secondary explanatory copy. |
eyebrow | ReactNode | ERROR | Mono uppercase eyebrow above the title. |
icon | ReactNode | — | Optional decorative icon slot, rendered above the eyebrow. |
onRetry | (() => void) | — | When provided, renders a retry button that calls this handler. |
retryLabel | string | Retry | Label for the retry button. |
style | CSSProperties | — | |
title | ReactNode | Something went wrong | Sentence-case headline. |