Empty State
Centered placeholder for empty lists or zero-result views: optional icon, required title, optional description, and slotted actions.
Preview
No results
Try adjusting your filters.
<EmptyState title="No results" description="Try adjusting your filters." /> Sizes
No results
Try adjusting your filters.
No results
Try adjusting your filters.
No results
Try adjusting your filters.
No results
Try adjusting your filters.
<EmptyState size="xs" title="No results" description="Try adjusting your filters." />
<EmptyState size="sm" title="No results" description="Try adjusting your filters." />
<EmptyState size="md" title="No results" description="Try adjusting your filters." />
<EmptyState size="lg" title="No results" description="Try adjusting your filters." /> Icon & actions
Pass an icon via the icon slot and action buttons via the default slot. The title alone is enough for a minimal state.
No documents yet
Upload your first document to get started.
<EmptyState title="No documents yet" description="Upload your first document to get started.">
<svg slot="icon">…</svg>
<button type="button">Upload document</button>
</EmptyState> API
| Prop | Type | Default | Description |
|---|---|---|---|
title | string | required | Required. The primary heading of the empty state. |
description | string | — | Optional supporting line below the title. |
size | 'xs' | 'sm' | 'md' | 'lg' | 'md' | Scales icon, text, and padding. |
Slots
| Prop | Type | Default | Description |
|---|---|---|---|
icon | slot | — | Inline SVG, scaled to the size variant. |
default | slot | — | Action buttons rendered below the description. |
Accessibility
- The title renders as an
<h3>— keep heading order coherent within the page. - The icon is decorative (
aria-hidden); meaning comes from the title and description. - Action buttons in the default slot keep native button semantics and focus order.
Theming surface
Every public token this component reads, extracted from its source at build time,
split by blast radius: the hooks that belong to this component,
then the shared and system tokens it consumes alongside everything else. The
Resolves to column walks each token's real lineage —
component → semantic → primitive → raw value — so you
can see exactly where re-pointing it lands. A spoke re-skins by overriding these in
its theme-<slug>.css; component internals are never edited. No
hook for what you need? File it with /request-lego.
Type roles it names
This component does not set a size, weight, face or tracking of its own — it names a composite, and the composite carries all five. A composite is a class rather than a token, so none of it appears in the tables below; re-theme text by re-pointing the token family, not by editing the component.
| Role | Re-point | Reach |
|---|---|---|
.typography-body-md |
| +28 more componentsesa-app-shell, esa-breadcrumbs, esa-card, esa-checkbox, esa-checkbox-group, esa-collapsible, esa-combobox, esa-command-palette, esa-confirm-dialog, esa-container, esa-danger-zone, esa-dialog, esa-dropdown-menu, esa-entity-search, esa-form-field, esa-header-nav, esa-input-tag, esa-nav-dropdown, esa-page-header, esa-popover, esa-progress-bar, esa-radio-group, esa-select, esa-side-dialog, esa-snackbar-item, esa-switch-toggle, esa-tab-layout, esa-textarea |
.typography-body-sm |
| +20 more componentsesa-alert-box, esa-breadcrumbs, esa-button-toggle, esa-card, esa-checkbox-group, esa-combobox, esa-command-palette, esa-date-picker, esa-file-upload, esa-form-field, esa-input-tag, esa-link-column, esa-loading-overlay, esa-progress-bar, esa-radio-group, esa-search-panel, esa-select, esa-stat, esa-text-field, esa-textarea |
.typography-body-xs |
| +14 more componentsesa-breadcrumbs, esa-checkbox, esa-checkbox-group, esa-combobox, esa-command-palette, esa-entity-search, esa-field-error, esa-header-nav, esa-input-tag, esa-progress-bar, esa-radio-group, esa-search-panel, esa-select, esa-textarea |
.typography-label-md |
| +33 more componentsesa-app-bar, esa-breadcrumbs, esa-button-group, esa-button-toggle, esa-card, esa-checkbox-group, esa-color-picker, esa-combobox, esa-command-palette, esa-confirm-dialog, esa-danger-zone, esa-date-picker, esa-dialog, esa-entity-search, esa-file-upload, esa-filter-container, esa-form-field, esa-header-nav, esa-input-tag, esa-link-column, esa-page-header, esa-pillbox, esa-popover, esa-progress-bar, esa-radio-group, esa-range-slider, esa-select, esa-side-dialog, esa-sidebar-nav, esa-stat, esa-text-field, esa-textarea, esa-tooltip |
.typography-label-md-strong |
| +3 more componentsesa-avatar, esa-error-summary, esa-header-nav |
.typography-label-sm-strong |
| +2 more componentsesa-alert-box, esa-collapsible |
.typography-label-xs-strong |
| +1 more componentesa-avatar |
.typography-title |
| +4 more componentsesa-avatar, esa-confirm-dialog, esa-dialog, esa-side-dialog |
Wired to this component
No other component reads these — re-point them and esa-empty-state is the
only thing that moves.
| Token | Tier | Resolves to |
|---|---|---|
--empty-state-icon-size-lg | component | 64px |
--empty-state-icon-size-md | component | 48px |
--empty-state-icon-size-sm | component | 32px |
--empty-state-icon-size-xs | component | 24px |
Shared tokens it reads
Owned by a family or by the semantic layer. Still overridable — but scope the
override (esa-foo { --form-border-color: … }) unless you mean to move
everything that reads it. Reach says what else that is.
| Token | Reach | Resolves to |
|---|---|---|
--color-content-default | semantic every component | #202020 via --color-gray-12 |
--color-content-default-secondary | semantic every component | #646464 via --color-gray-11 |
--spacing-100 | primitive every component | 0.25rem |
--spacing-200 | primitive every component | 0.5rem |
--spacing-300 | primitive every component | 0.75rem |
--spacing-400 | primitive every component | 1rem |
--spacing-600 | primitive every component | 2rem |
--spacing-800 | primitive every component | 4rem |