Source: https://monarchui.dev/components/empty-state/

Components · Status and feedback

# EmptyState

What will appear here, and the action that creates it.

@rookcom/monarch-react

Component

## First run

## Usage examples

### Filtered and all clear

### One line

## Purpose

Shown when there is nothing to display: says what will appear and offers the action that creates it, or a way back when a filter matched nothing.

### Use when

- A list, table or view has no items yet (`kind="first-run"`, the default).
- A search or filter returns nothing (`kind="filtered"` with `onClearFilters`).
- A queue is empty because the work is done (`kind="success"`).
- A dashboard section or table body is empty and needs a one-line note (`layout="line"`, `size="compact"`).

### Avoid when

- Loading failed: use ErrorState or InlineErrorRetry.
- Content is still loading: use LoadingState.
- A whole route is missing or forbidden: use ErrorPage.

## Design principles

- Say what will appear here, then offer the action that creates it. The product writes every word; the component only lays it out.
- First-run and filtered states read differently: first-run explains and invites, filtered offers a way back (Clear filters) rather than a create action.
- The heading level is configurable so the state fits the page outline; `p` avoids a heading inside a table cell.
- One accent-tinted icon tile at most; `success` switches it to the success tint. The rest stays neutral.

## API reference

```tsx
import { EmptyState } from '@rookcom/monarch-react';
```

| Export | Kind | Rendering | Root class |
| --- | --- | --- | --- |
| `EmptyState` | component | server-safe | `.mn-empty-state` |

All native `<div>` attributes pass through, and the ref is forwarded to the root `<div>`.

`children` render after the actions (for example a short list of next steps); `tip` renders last in small muted text.

Generated from the TypeScript declarations of `packages/react/src/components/feedback/empty-state`.

## Properties

### `EmptyState`

Shown when there is nothing to display yet. Say what will appear here and offer the action that creates it.

| Prop | Type | Description |
| --- | --- | --- |
| `title` required | `ReactNode` |  |
| `actions` | `ReactNode` | Primary and secondary actions, usually Buttons. |
| `children` | `ReactNode` | Extra content after the actions, such as a list of next steps. |
| `className` | `string` |  |
| `clearFiltersLabel` | `string` | Label of the clear-filters button. Default "Clear filters". |
| `description` | `ReactNode` |  |
| `headingLevel` | `EmptyStateHeadingLevel` | Title element. Default `h3`. Match the page outline (no skipped levels); use `p` where a heading would be wrong, such as inside a table cell. |
| `icon` | `ReactNode` | Decorative icon in a tinted tile. Hidden from assistive technology. |
| `id` | `string` |  |
| `kind` | `EmptyStateKind` | Why the view is empty. Exposed as `data-kind`. Default `first-run`. |
| `layout` | `"stack" \| "line"` | `stack` (default) centres icon, text and actions in a column; `line` is a single left-aligned row (icon, text, actions) for a list or dashboard section that has nothing in it. |
| `onClearFilters` | `(() => void)` | With `kind="filtered"`, renders a "Clear filters" button before any other actions. The product resets its own filters. |
| `size` | `"inline" \| "page" \| "compact"` | `inline` (default) sits inside a card or table; `page` fills an empty view; `compact` is a tighter block for table bodies, side panels and small cards. |
| `tip` | `ReactNode` | A short hint under the actions, such as a keyboard shortcut (compose it with Kbd). Shown in small muted text. |

## Variants

| Prop | Values | Description |
| --- | --- | --- |
| `size` | `'inline'``'page'``'compact'` | `inline` (default) sits inside a card or table; `page` adds vertical padding to fill a view; `compact` tightens padding, icon and title for table bodies and side panels. Exposed as `data-size`. |
| `layout` | `'stack'``'line'` | `stack` (default) centres everything in a column; `line` is one left-aligned row of icon, text and actions. `line` is exposed as `data-layout="line"`. |
| `kind` | `'first-run'``'filtered'``'success'` | `filtered` adds a Clear filters button when `onClearFilters` is set; `success` uses the success tint and a check icon when no `icon` is given. Non-default kinds are exposed as `data-kind`. |
| `headingLevel` | `'h1'``'h2'``'h3'``'h4'``'h5'``'h6'``'p'` | Title element. Default `h3`. |

## States

| State | Exposed as | Behaviour |
| --- | --- | --- |
| First run | – | The default: icon, title, description and actions. |
| Filtered | `data-kind="filtered"` | Clear filters (secondary button, label `clearFiltersLabel`) is rendered before the other actions. |
| Success | `data-kind="success"` | Success-tinted icon tile; a check icon when no icon is passed. |

## Interaction behaviour

- Clear filters calls `onClearFilters`; the product resets its own filters and the list re-renders.
- Other actions keep their own behaviour.

## Keyboard support

| Keys | Action |
| --- | --- |
| Tab / Shift+Tab | Moves through Clear filters and the actions, in that order. |
| Enter / Space | Activates the focused action. |

The empty state itself is not focusable.

## Accessibility

Role

none; the title is a heading (or a paragraph with

headingLevel="p"

)

Accessible name

–

- The icon tile is `aria-hidden`; meaning is carried by the title.
- The title is a real heading (h3 by default); pick the level that continues the page outline instead of skipping levels.
- No live region: an empty state is page content, not an announcement. When it replaces results after a filter change, the product announces the result count itself.
- Verified with axe in jsdom (each kind and layout) and in Chromium, Firefox and WebKit on /components/empty-state.

## Responsive behaviour

- `stack` is centred with a 28rem maximum width; text wraps at narrow widths and actions wrap onto new lines.
- `line` wraps: the text takes the remaining width (12rem minimum) and the actions drop below it at 320px.

## Theme support

- The icon tile uses the accent subtle and strong tokens, so it follows the product accent; `success` uses the success tokens.
- Colours are Monarch semantic tokens only, so light, dark and more-contrast themes apply automatically.

## Do and don't

### Do

- Describe what will appear, not that nothing is there.
- Offer at most one primary action.
- Use `kind="filtered"` when a filter is active, so the way out is Clear filters, not Create.
- Keep next-step logic (which action to suggest) in the product and pass the result as `actions`.

### Don't

- Do not use an empty state to report an error.
- Do not reuse the first-run copy for a filtered result: "No tickets yet" is wrong when tickets exist but are filtered out.

## Related components

- [ErrorState](https://monarchui.dev/components/error-state/): Something failed to load.
- [LoadingState](https://monarchui.dev/components/loading-state/): Content is on its way.
- [InlineErrorRetry](https://monarchui.dev/components/inline-error-retry/): One section failed and can be retried in place.
- [ErrorPage](https://monarchui.dev/components/error-page/): A whole route is missing, forbidden or down.

## Dependencies

| Kind | Depends on |
| --- | --- |
| npm packages | `react` |
| Monarch components | [Button](https://monarchui.dev/components/button/) |

## Testing coverage

- Renders the title as an h3 by default; `headingLevel` changes it, `p` renders no heading.
- Default props keep the 0.3 markup (no `data-kind` or `data-layout`; decorative icon).
- Filtered renders Clear filters first and activates it from the keyboard; other kinds never render it.
- Success shows a check icon unless one is passed; `line` and `compact` are exposed as data attributes.
- Tip and children render after the actions; the ref is forwarded.

| Check | Covered |
| --- | --- |
| Axe in jsdom | Yes |
| Server rendering | Yes |
| Browser suites | Visual baselines: /__gallery; Cross-browser axe: /components/empty-state |

Tests: `packages/react/src/components/feedback/empty-state`

## All example code

### First run

```tsx
import { Button, EmptyState, Kbd } from '@rookcom/monarch-react';

// PlusIcon: demo icon from the docs site; use your own.

<EmptyState
  icon={<PlusIcon />}
  title="No tickets yet"
  description="New tickets from your inbox and portal appear here."
  actions={
    <>
      <Button variant="primary">New ticket</Button>
      <Button>Import</Button>
    </>
  }
  tip={
    <>
      Press <Kbd>N</Kbd> to create a ticket from anywhere.
    </>
  }
/>
```

### Filtered and all clear

```tsx
import { EmptyState } from '@rookcom/monarch-react';

<EmptyState
  kind="filtered"
  size="compact"
  title="No results match these filters"
  description="Try a different search or clear the filters."
  onClearFilters={() => {}}
/>
<EmptyState
  kind="success"
  size="compact"
  title="You're all caught up"
  description="New items will appear here."
/>
```

### One line

```tsx
import { Button, EmptyState } from '@rookcom/monarch-react';

// ListIcon: demo icon from the docs site; use your own.

<EmptyState
  layout="line"
  icon={<ListIcon />}
  headingLevel="p"
  title="No activity this week"
  description="Changes to records show up here."
  actions={<Button size="sm">View all activity</Button>}
/>
```
