# EmptyState

> A centered placeholder for empty views — no results, first run, a cleared list.

URL: https://apogee.avaui.dev/docs/empty-state
Last updated: 2026-10-15

A centered placeholder for empty views — no results, first run, a cleared list.

Last updated October 15, 2026

```astro
<EmptyState title="No messages yet" description="When someone sends you a message, it'll show up here.">
<Inbox slot="icon" />
<Button slot="actions" size="small">Compose</Button>
</EmptyState>
```

## Add

```sh
npx @ava-ui/apogee add empty-state
```

No Apogee dependencies. Commonly composed with `Button` in the `actions` slot and a `@lucide/astro` icon in the `icon` slot.

## Usage

```astro
---
import EmptyState from '@/components/apogee/EmptyState/EmptyState.astro'
---
<EmptyState title="No results" description="Try a different search." />
```

## Anatomy

-   **`icon`** slot (optional) — a leading glyph above the title. A bare SVG is sized by `size` (`[&>svg]`); a FeaturedIcon keeps its own size. The `pattern` decoration is drawn behind this slot.
-   **Title** — required, always rendered.
-   **Description** — optional, rendered under the title when passed.
-   **`actions`** slot (optional) — buttons or links, wrapped and centered below the text.

```astro
<EmptyState title="No projects" description="Create your first project to get started.">
  <FolderOpen slot="icon" />
  <Button slot="actions" size="small">New project</Button>
  <Button slot="actions" variant="ghost" color="neutral" size="small">Import</Button>
</EmptyState>
```

## API

`@composable` — exposed to Content Editors in AVA. Any other attribute passes through to the root `<div>`.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `title` | `string` | — | Required. The primary message. |
| `description` | `string` | — | Optional supporting line, shown under the title. |
| `as` | `'p' \| 'h2' \| 'h3'` | `'p'` | Element for the title; use a heading when the empty state is the page’s only content. |
| `variant` | `'bordered' \| 'plain'` | `'bordered'` | `plain` drops the dashed box, for use inside a card, a table body or a full page. |
| `size` | `'small' \| 'medium' \| 'large'` | `'medium'` | Scales the icon, text and padding. |
| `pattern` | `'none' \| 'rings' \| 'grid'` | `'none'` | A faint decoration behind the icon, drawn with the border token under a radial fade. |
| `class` | `string` | — | Extra classes on the root, merged last. |

## Variants

Every `size`:

```astro
<EmptyState size="small" title="No results" description="…"><SearchX slot="icon" /></EmptyState>
<EmptyState size="medium" title="No results" description="…"><SearchX slot="icon" /></EmptyState>
<EmptyState size="large" title="No results" description="…"><SearchX slot="icon" /></EmptyState>
```

`plain` inside a card, where the dashed box would be a second frame:

```astro
<Card padding="none">
<EmptyState variant="plain" title="No members yet" description="Invite someone to get started.">
  <Users slot="icon" />
  <Button slot="actions" size="small">Invite</Button>
</EmptyState>
</Card>
```

Every `pattern`, with a FeaturedIcon in the slot:

```astro
<EmptyState pattern="rings" title="No projects" description="…">
<FeaturedIcon slot="icon" theme="outline" color="neutral"><FolderOpen /></FeaturedIcon>
</EmptyState>
<EmptyState pattern="grid" title="No projects" description="…">
<FeaturedIcon slot="icon" color="primary"><FolderOpen /></FeaturedIcon>
</EmptyState>
```

A full page with a heading as the title:

```astro
<EmptyState as="h2" variant="plain" size="large" title="Your inbox is empty" description="…" />
```

## States

-   Static: no interactive states of its own. Buttons in `actions` keep their own hover, focus and disabled states.
-   With or without `actions`: leave the slot empty when the user has nothing to do next.

## Examples

Title only, no icon or actions:

```astro
<EmptyState title="Nothing here yet" />
```

No results after a search or filter:

```astro
<EmptyState title="No results found" description="Try adjusting your filters.">
<SearchX slot="icon" />
</EmptyState>
```

## Accessibility

Native semantics, no extra ARIA needed — a heading-less block of text and optional interactive children (icon is `aria-hidden` by convention if decorative; pass `aria-hidden="true"` on the icon yourself, since the component doesn’t add it). Any `Button`/link placed in `actions` keeps its own keyboard behaviour.

## Tokens

| Token | Used for |
| --- | --- |
| `--border-neutral` | Dashed border around the block, and the `pattern` lines |
| `--foreground-neutral` | Title text |
| `--foreground-neutral-faded` | Icon colour and description text |
| `--radius-lg` | Corner radius |

## Do / Don’t

-   **Do** pair the title with a next step in `actions` when there’s one obvious action.
-   **Do** keep the description to one sentence explaining why the view is empty.
-   **Don’t** stack more than two actions — pick the primary path.
-   **Don’t** use it for error states with a retry — that reads better as an `Alert`.

## Content

-   Title is a short noun phrase stating what’s missing (“No messages yet”, “No results found”), not an apology.
-   Description (if any) explains why or what to do next, one sentence, no ending period.
-   Action label follows Button’s content rules (verb + outcome, sentence case).

## Related

[

AlertAn inline message with a status color and icon — info, success, warning, or critical; inline, filled or as a full-width banner.

](https://apogee.avaui.dev/docs/alert)[

SkeletonA pulsing placeholder shown in place of content while it loads. Zero JS.

](https://apogee.avaui.dev/docs/skeleton)[

DismissibleWrap anything with a close button, with an optional auto-dismiss timeout.

](https://apogee.avaui.dev/docs/dismissible)
