# Error State

Composable failure feedback with centered and inline layouts, recovery actions, and opt-in support details.

> For the complete documentation index, see [llms.txt](/llms.txt). Markdown variants are available by appending `.md` to any URL or sending an `Accept: text/markdown` header. An agent skill is available at [/.well-known/agent-skills/site-skill.md](/.well-known/agent-skills/site-skill.md).





## Playground [#playground]

```tsx
import { CircleAlertIcon } from "lucide-react";
import { Button } from "@/components/ui/button";
import { ErrorState, ErrorStateContent, ErrorStateHeader, ErrorStateTitle, ErrorStateDescription, ErrorStateMedia, ErrorStateActions } from "@/components/ui/error-state";

export const ErrorStateDemo = () => (
  <ErrorState variant="centered" border="none">
    <ErrorStateMedia><CircleAlertIcon aria-hidden="true" /></ErrorStateMedia>
    <ErrorStateContent>
      <ErrorStateHeader>
        <ErrorStateTitle>{"Unable to load projects"}</ErrorStateTitle>
        <ErrorStateDescription>{"Check your connection and try again."}</ErrorStateDescription>
      </ErrorStateHeader>
      <ErrorStateActions>
        {/* Connect onClick to your application's recovery operation. */}
        <Button type="button" variant="outline" size="sm">Try again</Button>
      </ErrorStateActions>
    </ErrorStateContent>
  </ErrorState>
);
```

## Installation [#installation]

Initialize shadcn in your React project, then install this component from the Vandor UI registry:

```bash
npx shadcn@latest add https://vandor-ui.vercel.app/r/error-state.json
```

## Dependencies [#dependencies]

Installed automatically by the registry CLI. For manual setup:

```bash
npm install cn lucide-react
```

## Usage [#usage]

```tsx
import {
  ErrorState,
  ErrorStateContent,
  ErrorStateHeader,
  ErrorStateTitle,
  ErrorStateDescription,
} from "@/components/ui/error-state";

<ErrorState>
  <ErrorStateContent>
    <ErrorStateHeader>
      <ErrorStateTitle>Unable to load projects</ErrorStateTitle>
      <ErrorStateDescription>
        Check your connection and try again.
      </ErrorStateDescription>
    </ErrorStateHeader>
  </ErrorStateContent>
</ErrorState>;
```

Put optional `ErrorStateMedia` before `ErrorStateContent`. Inside Content, use Header for Title and Description, Actions for buttons or links, and Details for safe support information. All parts are optional and accept native attributes and React 19 refs. The root selects layout; it does not fetch data, catch errors, or retry operations.

## Examples [#examples]

### Recovery action [#recovery-action]

Replace only the content that failed, keeping navigation and other working sections available. This demo switches to a successful preview when you select Try again, without making a network request. In your application, connect the action to a refetch or another recovery operation. Keep retry unavailable while that operation is pending and announce the outcome.

<ComponentPreview name="error-state-demo" title="error-state-demo.tsx">
  <ErrorStateDemo />
</ComponentPreview>

Install [Button](/docs/components/button) separately for examples or generated playground code using it. Icons use the declared `lucide-react` dependency; Error State does not require Button.

### Inline failure and support reference [#inline-failure-and-support-reference]

Use `variant="inline"` for a localized failure beside content that still works. Set `border="dashed"` for a subtle dashed outline, `border="solid"` for a solid outline, or leave the default `border="none"`. Border style and layout are independent. Do not offer retry for missing permissions or a missing page unless retry can actually fix the problem; offer an appropriate navigation or access-request action instead.

<ComponentPreview name="error-state-inline-demo" title="error-state-inline-demo.tsx">
  <ErrorStateInlineDemo />
</ComponentPreview>

Details now uses [Accordion](/docs/components/accordion) with a Plus/Minus crossfade and animated panel expansion. It is closed by default. Its default summary is “Technical details”; pass a localized `summary` when needed. Replace its `icon` and `expandedIcon` with decorative React nodes, or pass `icon={null}` to hide the indicator. Use `defaultOpen` for an initially open uncontrolled disclosure, or `open` with `onOpenChange` for controlled state. `disabled` prevents interaction.

This replaces the initial native `details` implementation: migrate native `onToggle` to `onOpenChange`, and move detail-element refs/attributes to the new div-based Accordion root. No native `details` or `summary` elements are rendered.

Only provide a safe support reference or explicitly sanitized content. Closed content may remain mounted while an animation completes and consumer code always contains any supplied information: the disclosure is **not a privacy or redaction boundary**. Never put secrets, raw server messages, or production stack traces inside it.

### Rendering errors with an optional boundary [#rendering-errors-with-an-optional-boundary]

Install the optional package only if your application needs a component-level React boundary:

```bash
pnpm add react-error-boundary
```

The integration below was checked with `react-error-boundary` 6.1.6. Simulate render error causes the child to throw during render; Try again resets both the boundary and the state that caused the failure. It intentionally does not show the exception message. React development tooling may log the simulated error even though the fallback handles it.

<ComponentPreview name="error-state-boundary-demo" title="error-state-boundary-demo.tsx">
  <ErrorStateBoundaryDemo />
</ComponentPreview>

For production reporting, connect the boundary's `onError` to your error reporting service. Report privately rather than exposing raw errors to the user. Resetting a boundary retries rendering; it does not automatically retry a failed request or repair its cause.

Boundaries catch descendant rendering errors, not ordinary event-handler exceptions, SSR errors, or arbitrary rejected promises. Handle request errors explicitly and render Error State from your application's error state. Use the library's supported hooks when deliberately forwarding a caught async error to a boundary. Next.js applications can also use this UI in their framework-provided route error fallback; Error State imports no Next.js APIs.

### Consumer setup and optional stories [#consumer-setup-and-optional-stories]

Error State requires React 19 and Tailwind CSS 4. The CLI installs `cn`, `lucide-react`, and the Accordion registry dependency (which declares `@base-ui/react`, `motion`, `cn`, and `lucide-react`). Load your global Tailwind stylesheet with shadcn semantic tokens including `foreground`, `muted`, `muted-foreground`, `background`, `border`, `destructive`, and `ring`. No provider or extra animation CSS is needed. The visual root remains server-compatible; Details is a separate client module. State, event handlers, and boundary integrations belong in a client module in React Server Component applications.

The registry installs `error-state.tsx` and `error-state-details.tsx`, plus Accordion, into the UI alias configured in `components.json`. Installing it can update an existing Accordion, so review the CLI overwrite prompt before replacing local customizations. It does not install Empty, Button, or an error boundary. Use CLI installation: copying the displayed Source alone is insufficient because Details is a separate supporting file. Examples and playground adapters are website-only, not distributed source.

Install Error State first, then optionally install its portable stories:

```bash
npx shadcn@latest add https://vandor-ui.vercel.app/r/error-state-stories.json
```

Use your existing `@storybook/react` setup with matching Storybook package versions (types and composition checked with 10.6.1). Discover `**/*.stories.tsx` in your UI directory and load your global theme stylesheet in the preview. Stories use native action buttons, inherit the consumer theme, and use the component's declared icon dependency without extra addons or providers. The stories-only installation does not reinstall the component or modify Storybook configuration. This project has no Storybook server; Storybook UI appearance is not verified here.

## Behavior & Accessibility [#behavior--accessibility]

Error State is presentational. Keep loading, failed requests, missing permissions, missing pages, and genuinely empty results distinct. Content and actions are caller-owned; no automatic announcements, focus changes, hardcoded icon, or built-in retry callback are added.

The root and layout parts render `div` elements. Title is visual text, not an automatic heading. For a named region, set `role="region"` and `aria-labelledby` on the root and a matching `id` on Title; add `role="heading"` and an appropriate `aria-level` to Title when the page hierarchy requires it. Hide decorative icons using `aria-hidden="true"`. Use native buttons for actions and links for navigation; do not rely on red alone to communicate the error.

Choose application-level announcements according to urgency, usually a polite live region for async feedback. Do not make every static fallback an assertive alert. When content containing focus is replaced, manage focus from the application and return it to an appropriate destination after recovery.

Details inherits Accordion's button/heading/panel semantics and keyboard interaction. It supports controlled `open`/`onOpenChange` and uncontrolled `defaultOpen`. Opening animates the panel and Plus/Minus indicators together; closing content becomes inert and hidden from assistive technology immediately. Reduced-motion preferences make transitions immediate. Long text wraps and the support content is keyboard-scrollable.

Playground title and description are composed children. Icon, retry, and details toggles are demo controls, not ErrorState props. The playground retry is intentionally inert; the runnable examples demonstrate recovery. Reset restores configuration and remounts the preview, closing the disclosure.

## Props [#props]

| Prop         | Type                          | Default             | Description                                                                                                                      |
| ------------ | ----------------------------- | ------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| border       | "none" \| "solid" \| "dashed" | "none"              | ErrorState: optional solid or dashed outline using the theme border token.                                                       |
| children     | ReactNode                     | Not set             | All parts: explicit content. Details does not redact supplied content; only provide safe, non-sensitive information.             |
| className    | string                        | Not set             | All parts: classes merged with defaults through cn. The root has no border by default.                                           |
| defaultOpen  | boolean                       | false               | ErrorStateDetails: initial uncontrolled open state.                                                                              |
| description  | ReactNode (composition)       | Not set             | ErrorStateDescription children. Explain a useful recovery step without blaming the user.                                         |
| disabled     | boolean                       | false               | ErrorStateDetails: disables disclosure interaction.                                                                              |
| expandedIcon | ReactNode                     | MinusIcon           | ErrorStateDetails: expanded-state indicator. Crossfades with the closed icon.                                                    |
| icon         | ReactNode                     | PlusIcon            | ErrorStateDetails: closed-state indicator React node. null hides the indicator.                                                  |
| onOpenChange | (open: boolean) => void       | Not set             | ErrorStateDetails: requests an open-state change when the Accordion trigger is activated.                                        |
| open         | boolean                       | Uncontrolled        | ErrorStateDetails: controlled open state; pair with onOpenChange. No details are rendered unless composed explicitly.            |
| role         | AriaRole                      | Not set             | ErrorState: choose a region or announcement policy at the application level. No automatic alert, focus movement, or live region. |
| showAction   | boolean (demo only)           | true (demo)         | Demo composition: includes an inert retry button. Connect its onClick to your application's recovery operation.                  |
| showDetails  | boolean (demo only)           | false (demo)        | Demo composition: includes an initially closed Accordion disclosure with a safe example reference.                               |
| showMedia    | boolean (demo only)           | true (demo)         | Demo composition: includes ErrorStateMedia with a decorative CircleAlert icon.                                                   |
| summary      | ReactNode                     | "Technical details" | ErrorStateDetails: accessible disclosure label. Keep it nonempty and free of interactive children.                               |
| title        | ReactNode (composition)       | Not set             | ErrorStateTitle children, not a prop on ErrorState. Renders a div; supply heading semantics when appropriate.                    |
| variant      | "centered" \| "inline"        | "centered"          | ErrorState: centered replacement content or a compact, leading-icon inline layout.                                               |

The reference lists component-specific props and selected native props, not the complete React API. Other native attributes are forwarded to the rendered element.

## Source [#source]

```tsx
export {
  ErrorState,
  ErrorStateActions,
  ErrorStateContent,
  ErrorStateDescription,
  ErrorStateHeader,
  ErrorStateMedia,
  ErrorStateTitle,
} from "./error-state-base";
export type { ErrorStateProps } from "./error-state-base";
export { ErrorStateDetails } from "./error-state-details";
export type { ErrorStateDetailsProps } from "./error-state-details";

```

## Credits [#credits]

* [shadcn/ui Empty](https://ui.shadcn.com/docs/components/base/empty): Composition and visual vocabulary reference; Error State is an independent implementation, not an adapted upstream error component.

* [react-error-boundary](https://github.com/bvaughn/react-error-boundary): Optional MIT-licensed dependency used only by the rendering-error integration example.
