Empty State
An empty state fills a region that has no data yet: an empty list, zero
search results, a first-run screen. It centres an optional icon in a
muted disc, a title, a description and a row of actions, and scales all
of them with one size.
import EmptyState from '@zuilib/components/empty-state'
Basic
The four parts are props. icon is decorative (aria-hidden); the
title carries the meaning.
Sizes
size scales the padding, the gap, the icon disc and the type together.
An SVG without a size-* class is sized to fit the disc.
Compound
The parts are also statics on EmptyState (and named exports). Use them
for custom order or extra content; they inherit size from the root.
titleAs / EmptyState.Title as render a heading tag when the empty
state should appear in the document outline.
Props
EmptyStateProps extends the <div> attributes of the root with:
| Prop | Type | Default | Description |
|---|---|---|---|
icon | ReactNode | — | Decorative glyph in a muted disc; aria-hidden |
title | ReactNode | — | |
titleAs | 'div' | 'p' | 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'div' | Element the prop title renders as |
description | ReactNode | — | |
actions | ReactNode | — | A centred, wrapping row of buttons / links |
size | 'sm' | 'md' | 'lg' | 'md' | Scales padding, gap, icon disc and type; inherited by the parts |
className | string | — | Merged last, after the size classes |
children | ReactNode | — | Rendered after the prop-driven parts; the compound parts go here |
Parts
| Prop | Type | Default | Description |
|---|---|---|---|
EmptyState.Icon | span props | — | The muted disc; aria-hidden. SVGs without a size-* class are sized for the size |
EmptyState.Title | div props + as?: EmptyStateTitleTag | as: 'div' | Pass a heading tag to put it in the outline |
EmptyState.Description | div props | — | Muted, max-w-prose; links inside are underlined foreground |
EmptyState.Actions | div props | — | Centred flex-wrap row |
Slots
| Slot | Element | Notes |
|---|---|---|
[data-slot="empty-state"] | div | Root. data-size |
[data-slot="empty-state-icon"] | span | The disc |
[data-slot="empty-state-title"] | div (or titleAs) | — |
[data-slot="empty-state-description"] | div | — |
[data-slot="empty-state-actions"] | div | — |
/* Consumer CSS: square icon disc in brand colour */
[data-slot="empty-state-icon"] {
border-radius: var(--radius-md);
background-color: var(--primary);
color: var(--primary-foreground);
}
Tokens
| Token | Used for |
|---|---|
--muted, --muted-foreground | Icon disc and description |
--foreground | Title and links in the description |
--spacing | Padding, gap and disc size are multiples of it |
--text-xs/sm/base/lg | Type per size |
Accessibility
- Static content with no live role. When the empty state replaces results that just changed (a search coming back empty), pass
role="status"so the change is announced. - The icon is
aria-hidden; make sure the title states what is empty. - The title is a
<div>by default because the component cannot know its outline level; usetitleAs="h2"(orasonEmptyState.Title) when it should be a heading. - Put the primary action first in
actions; the row is a normal tab sequence.