# Empty state

A lit, dithered illustration with a title, a description and an action, for views with nothing in them yet.

> 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. The dithercn agent skill, for building any dither on the engine, is at [/.well-known/agent-skills/dithercn/SKILL.md](/.well-known/agent-skills/dithercn/SKILL.md).

<ComponentPreview src="examples/empty-state-demo.tsx" size="lg" controls="emptyStateControls">
  <EmptyStateDemo />
</ComponentPreview>

## Installation [#installation]

<InstallBlock name="empty-state" />

## Usage [#usage]

```tsx
import { EmptyState } from "@/components/dithercn/empty-state";
import { Button } from "@/components/ui/button";

export function NoProjects() {
  return (
    <EmptyState
      illustration="folder"
      title="No projects yet"
      description="Create a project to start building."
      action={<Button>New project</Button>}
    />
  );
}
```

Above the text sits a small lit object drawn in dither cells. Each surface takes a tone from a light up and to the left, so faces turned to the light are dense, faces turned away are sparse, and curved surfaces step through every Bayer level. A one-cell seam splits the object's parts, a clear margin outlines it, and a faint halo and floor shadow sit behind it.

* `box`: an open box with its flaps folded back. The default, for lists and orders.
* `search`: a magnifying glass with a glint on the glass. For search and filters with no results.
* `folder`: a folder with a sheet peeking out. For files, projects and documents.
* `planet`: a lit sphere with a tilted ring. For feeds, events and anything still to arrive.

`matrix` changes the pattern: `bayer4` for crisp steps, `bayer8` for finer ones, `blue-noise` for an even grain.

## Examples [#examples]

### Illustrations [#illustrations]

The four objects in four colours.

<ComponentPreview src="examples/empty-state-illustrations.tsx" size="lg">
  <EmptyStateIllustrations />
</ComponentPreview>

### Inside a settings panel [#inside-a-settings-panel]

A first-run state for API keys, with the green folder and a dither button for the action.

<ComponentPreview src="examples/empty-state-panel.tsx" size="lg">
  <EmptyStatePanel />
</ComponentPreview>

### No search results [#no-search-results]

The magnifier in grey, for a state the user will see often. Keep these quiet and give a way out.

<ComponentPreview src="examples/empty-state-search.tsx" size="lg">
  <EmptyStateSearch />
</ComponentPreview>

## Props [#props]

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `title` (required) | `ReactNode` |  | The heading. Say what is empty, plainly. |
| `description` | `ReactNode` |  | One or two sentences: why it is empty and what to do next. |
| `action` | `ReactNode` |  | Buttons or links, under the text. |
| `illustration` | `"box" \| "search" \| "folder" \| "planet"` | `"box"` | The dithered object above the text. |
| `color` | `DitherColor` | `"ink"` | Colour of the illustration: a palette name, a CSS variable name or any CSS colour. |
| `matrix` | `"bayer2" \| "bayer4" \| "bayer8" \| "blue-noise"` | `"bayer4"` | The threshold pattern. |
| `animate` | `boolean` | `true` | Bob the object up and down by one cell. |
| `titleAs` | `"h2" \| "h3" \| "h4"` | `"h3"` | Heading level of the title. |

## Notes [#notes]

* The illustration is decorative and hidden from assistive technology. The title is a real heading; set `titleAs` to fit the page outline.
* The bob moves the object one whole cell in steps, so the dither never smears, and it stops under reduced motion. Each illustration is two SVG paths, computed once per illustration and matrix and cached, so it costs no JavaScript at runtime and renders on the server.
* The illustration keeps a 112 by 72 cell aspect ratio up to 336 pixels wide, so cells are 3 pixels at full size, and it shrinks on narrow screens.