HyEmptyState
Usage examples on this page are written for Lit / plain HTML (
<hy-empty-state>). The same component ships asHyEmptyStatein@whitespaceux/harmony-react(native React 19), with typed wrappers for Angular, Solid, Svelte, and Vue. The API reference below applies to all frameworks.
A placeholder for "no data / no results / error / loading" regions.
Collapses the hand-rolled empty + load-state markup duplicated across an app (icon/illustration + heading + body + actions) into one element. A state axis (empty | loading | error) absorbs the {loading}…{error}…{empty} triplet with a built-in spinner and retry button; a variant axis presets the icon + copy per scenario.
Key Features:
- Scenario presets (
no-data,no-results,error,not-found,success,offline) - Async-state mode (
loading→ built-in spinner;error→ retry button firingretry) - Search-aware
no-resultsvia thequeryprop - Compact (inline) vs medium (full-region) sizes
- A design system can upload a brand image per variant, drawn in place of the icon on a medium empty state while the
mediaslot is empty - An illustration of the app's own in the
mediaslot (an<img>, the file for the surface's color mode chosen by the app) - Slots for media / actions / footer, and the default slot for a richer description (the heading is the
headingproperty)
Accessibility: built in
role="status"(implicitaria-live="polite") so a list becoming empty after filtering is announced to screen readers- High contrast + forced-colors support
Accessibility: your part
- Mark an
hy-iconin themediaslotdecorative. The heading already says what it means. - Do not move focus into the empty state unless the person's own action produced it.
When to use
- A region with nothing in it yet, with the action that fills it: no projects, no messages.
- A search or a filter that matches nothing, with
variant="no-results". - A region whose content failed to load, with
state="error"and a retry.
Do
- Say what people can do next in the description.
- Use
size="compact"in a panel, a row or a small region.
When not to use
- A save, rename, add or delete that failed while the content is still there:
hy-toastor an inlinehy-alert. - Loading a region whose layout is known:
hy-skeletonin the shape of the content. Keepstate="loading"for a small region or an unknown shape. - A condition about content that is still shown:
hy-alert. - A failed load with no function to re-run:
variant="error"alone, which shows no retry button. Usestate="error"only whenretryreloads the region.
Examples
No-data with an action
<hy-empty-state variant="no-data" heading="No design systems yet">
Create your first design system to get started.
<hy-button slot="actions" variant="brand">New design system</hy-button>
</hy-empty-state>Search-aware no-results
<hy-empty-state variant="no-results" query="brutalist"></hy-empty-state>The async triplet, one element
<hy-empty-state state="loading"></hy-empty-state>
<hy-empty-state state="error"></hy-empty-state>
<hy-empty-state state="empty" variant="no-data"></hy-empty-state>API
Properties
| Property | Attribute | Type | Default | Description |
|---|---|---|---|---|
variant | variant | 'no-data' | 'no-results' | 'error' | 'not-found' | 'success' | 'offline' | 'coming-soon' | 'no-data' | Scenario preset determining the default icon and copy. |
state | state | 'empty' | 'loading' | 'error' | 'empty' | Async-state mode. loading shows a spinner; error shows the error layout with a retry button. |
size | size | 'compact' | 'medium' | 'medium' | Size / density. compact for inline placements (empty rows, side panels); medium for centered full-region placements. |
heading | heading | string | undefined | — | Heading text. An empty string counts as not set: the variant's default heading shows (and, while loading, the default Loading… region label). |
description | description | string | undefined | — | Description text. The default slot wins if both are provided. An empty string counts as not set: the variant's default description shows. |
query | query | string | undefined | — | Search query. For variant="no-results", drives the default No results for "…" heading. |
label | label | string | undefined | — | Accessible label for the region (applied as aria-label). |
queryHeadingText | query-heading-text | string | 'No results for "{query}"' | The default variant="no-results" heading when query is set, a template: {query} is replaced by the query. Use for internationalization. |
loadingLabel | loading-label | string | 'Loading…' | The region's accessible name in state="loading" when no heading is set. Use for internationalization. |
retryLabel | retry-label | string | 'Retry' | Label of the built-in retry button (state="error"). Use for internationalization. |
strings | — | Partial<EmptyStateStrings> | {} | The built-in copy — each variant's default heading and description — merged over DEFAULT_EMPTY_STATE_STRINGS: set only the keys you translate. An explicit heading / description still wins. The query heading (query-heading-text), the loading name (loading-label) and the retry button (retry-label) stay separate properties. Property only (no attribute). Use for internationalization. |
Events
| Event | Detail | Description |
|---|---|---|
retry | — | Fired when the built-in retry button (shown in state="error") is clicked. Detail: {} |
Slots
| Slot | Description |
|---|---|
media | The visual — icon, illustration, or animation. Defaults to the variant's icon, or the design system's image for it. Leave it empty unless its content means something of its own (a 404's number): anything here hides that image. |
default | The description body (richer than the description prop). |
actions | Primary + secondary action buttons. |
footer | Help link / "learn more". |
CSS Parts
| Part | Description |
|---|---|
base | The empty-state region wrapper. |
media | The media (icon/illustration) container. |
title | The heading element. |
description | The description element. |
actions | The actions slot container. |
footer | The footer slot container. |
CSS Custom Properties
The documented hooks. Every override hook the component reads — one per token it paints — is listed in @whitespaceux/harmony/theme-api.json, and @whitespaceux/harmony/component-bindings.json (the Harmony MCP get-bindings tool) says which part and state each one paints.
| Property | Default | Description |
|---|---|---|
--hy-empty-state-padding-container-lg | var(--hy-padding-container-lg) | Padding around the region. |
--hy-empty-state-padding-container-sm | var(--hy-padding-container-sm) | Padding around the region. |
--hy-empty-state-gap-container-md | var(--hy-gap-container-md) | Gap between media / title / description / actions. |
--hy-empty-state-gap-container-sm | var(--hy-gap-container-sm) | Gap between media / title / description / actions. |
--hy-empty-state-foreground-default-rest | var(--hy-foreground-default-rest) | Heading color. |
--hy-empty-state-description-foreground-subtle-rest | var(--hy-foreground-subtle-rest) | Description / footer color. |
--hy-empty-state-media-foreground-subtle-rest | var(--hy-foreground-subtle-rest) | Default media icon color. |