# Error State

> A failed-generation state with a preserved prompt and retry.

A shadcn/ui block from the [@spectrumui](https://ui.spectrumhq.in) registry, listed on [shadcn.exchange](https://shadcn.exchange/r/spectrumui/error-state).

- Page, with a live preview: https://shadcn.exchange/r/spectrumui/error-state
- Screenshot: https://abzjhwgwpdpz7ua5.public.blob.vercel-storage.com/shots/spectrumui/error-state-light-db32797643778f6f.png
- Registry item: https://ui.spectrumhq.in/r/error-state.json
- Listed: 2026-10-13, source last changed 2026-10-13

## Install

```bash
npx shadcn@latest add https://ui.spectrumhq.in/r/error-state.json
```

Run it in a project that has shadcn/ui set up. It adds the files below and installs what they depend on.

## Dependencies

- npm packages: `lucide-react`

## Files

### `components/spectrumui/blocks/ai-assistants/error-state.tsx`

```tsx
'use client';

import { useState } from 'react';
import { AlertTriangle, Loader2, RefreshCw } from 'lucide-react';
import { cn } from '@/lib/utils';

export type ErrorStateVariant = 'Card' | 'Inline';

export interface ErrorStateProps {
  title?: string;
  message?: string;
  retryLabel?: string;
  retrying?: boolean;
  onRetry?: () => void;
  variant?: ErrorStateVariant;
  className?: string;
}

export function ErrorState({
  title = 'Generation failed',
  message = 'The model timed out before finishing. Your prompt is preserved — retrying usually resolves this.',
  retryLabel = 'Retry',
  retrying: retryingProp,
  onRetry,
  variant = 'Card',
  className,
}: ErrorStateProps) {
  const [retryingState, setRetryingState] = useState(false);
  const retrying = retryingProp !== undefined ? retryingProp : retryingState;

  function retry() {
    setRetryingState(true);
    setTimeout(() => setRetryingState(false), 1600);
    onRetry?.();
  }

  if (variant === 'Inline') {
    return (
      <div
        role="alert"
        className={cn(
          'flex w-fit items-center gap-2.5 rounded-full border border-red-500/20 bg-red-500/[0.06] py-1.5 pl-3 pr-1.5 text-[12.5px] text-red-700 dark:text-red-400',
          className,
        )}
      >
        <AlertTriangle className="size-3.5 shrink-0" />
        <span>{title}</span>
        <button
          type="button"
          onClick={retry}
          disabled={retrying}
          className="flex items-center gap-1 rounded-full bg-white px-2 py-0.5 text-[11.5px] font-medium text-neutral-800 shadow-xs transition-transform duration-150 active:scale-[0.95] focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-red-400 dark:bg-neutral-900 dark:text-neutral-200"
        >
          {retrying ? (
            <Loader2 className="size-3 animate-spin [animation-duration:800ms]" />
          ) : (
            <RefreshCw className="size-3" />
          )}
          {retryLabel}
        </button>
      </div>
    );
  }

  return (
    <div
      role="alert"
      className={cn(
        'w-full max-w-[420px] rounded-2xl border border-black/[0.08] bg-white p-4 shadow-xs dark:border-white/[0.09] dark:bg-[#0B0B0D]',
        className,
      )}
    >
      <div className="flex items-start gap-3">
        <span className="grid size-8 shrink-0 place-items-center rounded-full bg-red-500/10 text-red-600 dark:text-red-400">
          <AlertTriangle className="size-4" />
        </span>
        <div className="min-w-0">
          <h3 className="text-[13.5px] font-semibold tracking-[-0.1px] text-neutral-900 dark:text-neutral-50">
            {title}
          </h3>
          <p className="mt-1 text-[12.5px] leading-[1.6] text-neutral-500 dark:text-neutral-400">
            {message}
          </p>
        </div>
      </div>

      <div className="mt-3.5 flex justify-end">
        <button
          type="button"
          onClick={retry}
          disabled={retrying}
          className="flex items-center gap-1.5 rounded-lg bg-neutral-900 px-3 py-1.5 text-[12.5px] font-medium text-white transition-[transform,opacity] duration-[160ms] ease-[cubic-bezier(0.23,1,0.32,1)] active:scale-[0.97] disabled:opacity-60 focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-neutral-400 focus-visible:ring-offset-2 dark:bg-neutral-100 dark:text-neutral-900"
        >
          {retrying ? (
            <Loader2 className="size-3.5 animate-spin [animation-duration:800ms]" />
          ) : (
            <RefreshCw className="size-3.5" />
          )}
          {retrying ? 'Retrying…' : retryLabel}
        </button>
      </div>
    </div>
  );
}

export default ErrorState;
```
