Skip to contentAsheeUIAsheeUI

ErrorState

The presentation of a region that could not be loaded, what failed, and the retry beside the explanation.

A failure in a region is two things at once: something the reader has to understand, and something they have to do about it. ErrorState is the one surface in the library whose whole purpose is to say both, what failed, what it cost them, and, when there is anything left to try, the retry sitting beside the explanation rather than three screens away.

It is EmptyState with the error tone: the same centred block and the same action group, plus a retry and a disclosure for the message the request returned. It is drawn as a panel and announced as an alert by default, because it usually replaces content a moment after a request failed.

Usage

import { ErrorState } from "@asheeui/web";

<ErrorState
  title="Invoices could not be loaded"
  description="The request timed out. Trying again usually works."
  onRetry={refetch}
/>

onRetry is a React handler rather than a configured action, because a configured action describes a destination and "run that request again" is not one. Its presence is what draws the control: without onRetry the state offers no retry at all, which is the honest arrangement when nothing can be retried. The control it draws is a solid, danger-coloured button, which is what makes the retry the primary action of a failed region rather than one option among several.

description is where the reassurance goes, nothing has been charged, the draft is saved, because a reader who has just lost sight of their data is asking what it cost them before they ask how to get it back. retryLabel renames the control for the cases where "Try again" is too vague about what is being tried again, and both that label and detailLabel take a node rather than a string, so either control can carry an icon; what a reader hears is the text the node renders.

The technical detail

detail is the message a request returned, and it is written for a developer. It renders folded into a disclosure labelled by detailLabel and opened only when someone asks, so the reader who needs it can paste it into a support ticket and the reader who does not is never shown a stack trace. The disclosure is the platform's own <details>, so it opens without scripting and is announced with its open state.

When trying again is not the answer

primaryAction and secondaryAction are the same configurations EmptyState takes, and they share one group with the retry: the configured actions first, the retry after them. That order is deliberate, the configured pair is the consumer's own statement of where the reader can go and leads the row, while the retry is the framework's recovery path for the request that failed. When no configured action is given the retry is the only control, and therefore first. A permission failure is the case worth designing for, there is no request worth running again, so the state explains, and offers the two destinations that can actually help.

Props

size, panel, role, retryLabel and detailLabel resolve instance → components.errorstate → the framework's fallback, registered as md, true, alert, Try again and Technical details. The default panel and the default alert are one decision seen twice: this state usually replaces a region that was there a moment ago, so it draws the surface it replaced and says what happened. An ErrorState is dressed by components.errorstate alone, it composes the empty presentation but states its own density and its own surface, so restyling the one does not restyle the other.

title, titleAs, description, detail, onRetry, primaryAction, secondaryAction, icon, as and children are instance-only. There is no type: a failure is the error tone, and a component that could be told to look like a success is a component that would lie about the data.

Accessibility

  • The state is an alert by default, and that is a statement about timing. A reader mid-task is interrupted because a request they started has just failed: silence would leave them waiting for content that is not coming.
  • role="status" is the polite version, for a failure that is not the result of what the reader just did, a background refresh, a second region that failed while they were reading the first. It waits for a pause instead of cutting in.
  • role="none" renders no live region at all, for a failure that was part of the page from the start, such as a not-found page: there was no change, so there is nothing to announce.
  • The retry is a real <button type="button"> and the disclosure is a <summary> inside <details>, so both are keyboard-operable and both are reached by tabbing. Focus is never moved to either: the failure reports, and the reader decides when to act on it.
  • The title renders as a heading, an <h2> unless titleAs states another level, so a reader can reach the failure by heading navigation, the same way they reach a section. titleAs is how a failure that is the page renders an h1, and how one inside a card's h3 renders an h4.

The two channels, and why a retry never moves focus, are Announcements.