# Empty State

> What a list, table, or page shows when it has nothing in it yet: an icon, a title, a short description, and one or two actions.

- Name: `empty-state`
- Status: beta
- Categories: empty, zero state, no results, placeholder
- Files: the install writes `empty-state.tsx`, and any part files named `empty-state-*.tsx`, to your `components` alias, `@/components` by default. Edit them freely. They are yours.
- Other registry items: `@txbrendan/button`. The install adds them.
- Gallery: https://txbrendan.dev/blocks/empty-state
- Registry item: https://txbrendan.dev/r/empty-state.json

## Install

1. Add the registry to `components.json`. Do this first, once:

   ```bash
   npx shadcn@latest registry add "@txbrendan=https://txbrendan.dev/r/{name}.json"
   ```

   The command writes this entry. You can also add it by hand:

   ```json
   {
     "registries": {
       "@txbrendan": "https://txbrendan.dev/r/{name}.json"
     }
   }
   ```

   Without this step, the shadcn CLI looks up `@txbrendan` in a public list that txbrendan/ui does not control.

2. Add the theme once per app. It replaces your color tokens with ones checked for contrast:

   ```bash
   npx shadcn@latest add @txbrendan/theme
   ```

3. Add the block. It also adds every component it uses:

   ```bash
   npx shadcn@latest add @txbrendan/empty-state
   ```

## Use it for

- A list, table, or page that has nothing in it yet. Say what will show up here and how to add the first one.
- A search or filter that found nothing. Say so, and offer a way to clear the filter.
- A section that failed to load. Set tone="danger" and offer a Retry button.
- Pass one or two Buttons in actions, the main one first. With size="sm", pass size="sm" Buttons too.
- Make every action an emphasis="ghost" Button, so the next step stays quiet. With two actions, give the main one tone="brand" so it stands apart.
- Write the title so it makes sense without the icon, since screen readers skip the icon.

## Avoid it for

- Content that is still loading. Show a skeleton, then the empty state once you know there is nothing.
- A setup checklist for someone's first visit. Use First Use instead.
- A message about the whole app, like an outage or a billing problem. Use Alert.
- More than two actions. Pick the one that matters, and link to the rest from the description.

## Props

- `title`: `ReactNode`. What is missing, in a few words, like "No projects yet". Required.
- `description`: `ReactNode`. One or two short sentences: what will show up here, and how to start.
- `icon`: `ReactNode`. An icon element, like \<FolderIcon />. It sits in a round tile above the title.
- `actions`: `ReactNode`. One or two Buttons or links. Put the main one first. Below 18rem wide they stack.
- `tone`: `"neutral" | "brand" | "danger" | "success" | "warning"`. Default `"neutral"`. Color of the icon tile. Use "danger" when something failed to load.
- `size`: `"sm" | "md" | "lg"`. Default `"md"`. sm inside a list or card, md for a section, lg for a whole page. Scales the space, icon, and text.
- `headingLevel`: `1 | 2 | 3 | 4 | 5 | 6`. Default `2`. The title's heading level, so it fits under the heading above it.
- `className`: `string`. Extra classes on the outer div, which holds the container query. Add a border here to frame it.

## Accessibility

- The title is a real heading, an h2 by default. Set headingLevel to one below the heading the empty state sits under.
- The icon tile is hidden from screen readers, so the title has to say what is missing on its own.
- The block adds nothing that takes focus. Only the actions you pass are in the tab order, in the order you pass them.
- Other props, like role or aria-label, go on the outer div.

## Examples

### Nothing here yet

```tsx
<EmptyState
  icon={<FolderPlus />}
  title="No projects yet"
  description="Projects keep your tasks, files, and people in one place."
  actions={
    <>
      <Button tone="brand" emphasis="ghost">New project</Button>
      <Button emphasis="ghost">Import</Button>
    </>
  }
/>
```

### A search with no results

```tsx
<EmptyState
  size="sm"
  headingLevel={3}
  icon={<SearchX />}
  title="No matches"
  description="Nothing matches those filters."
  actions={<Button size="sm" emphasis="ghost">Clear filters</Button>}
/>
```

### Failed to load

```tsx
<EmptyState
  tone="danger"
  icon={<CircleAlert />}
  title="Could not load invoices"
  description="Check your connection, then try again."
  actions={<Button emphasis="ghost">Try again</Button>}
/>
```
