Feedback & Status
Empty State
A placeholder for a region that has nothing to show — an unfilled list, a search with no matches, a feature not set up yet. Pairs a glyph with a short heading, an explanation and the action that resolves it.
Preview
Section titled “Preview”<nana-empty-state title="No invoices yet" style="width: 100%">
Invoices you create will show up here.
<nana-button slot="actions" variant="primary" size="sm">
New invoice
</nana-button>
</nana-empty-state> Installation
Section titled “Installation”The empty state ships for all three targets. Pick the tab for your project — the CLI detects which you are on and serves the matching implementation.
npx @nana-tec/ui add empty-stateLands as src/components/ui/empty-state/empty-state.tsx, styled with Tailwind
classes that resolve to the design tokens. Requires Tailwind v4.
npx @nana-tec/ui add empty-stateLands in src/lib/components/ui/empty-state/ as EmptyState.svelte, styled with
Tailwind classes that resolve to the design tokens. Requires Tailwind v4.
Install the package for a versioned dependency:
npm install @nana-tec/ui-componentsOr copy the source in and own it:
npx @nana-tec/ui add empty-stateimport { EmptyState } from "@/components/ui/empty-state";import { Button } from "@/components/ui/button";
export function Example() { return ( <EmptyState title="No invoices yet" actions={<Button size="sm">New invoice</Button>}> Invoices you create will show up here. </EmptyState> );}title, icon and actions take nodes; the body copy is the children. A node
passed to icon replaces the default glyph, and noIcon drops it:
<EmptyState icon={<SearchIcon />} title="No results" size="sm" bordered> Nothing matched “quarterly”.</EmptyState><script lang="ts"> import { EmptyState } from '$lib/components/ui/empty-state'; import { Button } from '$lib/components/ui/button';</script>
<EmptyState title="No invoices yet"> Invoices you create will show up here.
{#snippet actions()} <Button size="sm">New invoice</Button> {/snippet}</EmptyState>title is a string; icon and actions are snippets. An icon snippet
replaces the default glyph, and noIcon drops it:
<EmptyState title="No results" size="sm" bordered> Nothing matched “quarterly”.
{#snippet icon()} <SearchIcon /> {/snippet}</EmptyState>import "@nana-tec/ui-components/empty-state";<nana-empty-state title="No invoices yet"> Invoices you create will show up here. <nana-button slot="actions" variant="primary" size="sm">New invoice</nana-button></nana-empty-state>Classes you pass to the React and Svelte components are merged with
tailwind-merge, so an override wins rather than fighting. See
Using Tailwind.
size scales the glyph, the type and the padding together — sm for a panel or
a popover, md for a card or a table body, lg for a page with nothing on it
yet.
<nana-empty-state size="sm" bordered title="Small" style="width: 100%">
Compact panels and popovers.
</nana-empty-state>
<nana-empty-state size="md" bordered title="Medium" style="width: 100%">
A card or a table body.
</nana-empty-state>
<nana-empty-state size="lg" bordered title="Large" style="width: 100%">
A whole page with nothing in it yet.
</nana-empty-state> No results
Section titled “No results”A search that matched nothing is the most common empty state, and it reads better with the query in the copy and a way back out of it.
<nana-empty-state
icon="search"
title="No results"
size="sm"
bordered
style="width: 100%"
>
Nothing matched “quarterly”. Try a broader term or clear the filters.
<nana-button slot="actions" variant="ghost" size="sm">
Clear filters
</nana-button>
</nana-empty-state> Custom glyph
Section titled “Custom glyph”Anything in the icon slot replaces the bundled glyph — another icon set, a
small illustration, a logo.
<nana-empty-state title="Nothing archived" style="width: 100%">
<svg
slot="icon"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="1.5"
aria-hidden="true"
>
<rect x="2" y="4" width="20" height="5" rx="1" />
<path d="M4 9v9a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9" />
<path d="M10 13h4" stroke-linecap="round" />
</svg>
Items you archive are kept here for 30 days.
</nana-empty-state> In a table
Section titled “In a table”Table has its own empty slot for the placeholder that
spans the columns. Drop an empty state into it so the message matches the rest
of the app.
<nana-table empty style="width: 100%">
<nana-table-header>
<nana-table-row>
<nana-table-head>Invoice</nana-table-head>
<nana-table-head>Customer</nana-table-head>
<nana-table-head>Amount</nana-table-head>
</nana-table-row>
</nana-table-header>
<nana-empty-state slot="empty" size="sm" title="No invoices">
Invoices you create will show up here.
</nana-empty-state>
</nana-table> In a select or combobox
Section titled “In a select or combobox”Select and Combobox both expose
an empty slot for the message their panel shows when nothing is left to pick.
Slot an empty state in and the panel drops its own padding so the two do not
stack.
<nana-combobox label="Country" placeholder="Type to filter…"> <nana-option value="ke">Kenya</nana-option>
<nana-empty-state slot="empty" size="sm" icon="search" title="No countries"> Nothing matched that search. </nana-empty-state></nana-combobox>Accessibility
Section titled “Accessibility”The host takes role="status" and aria-live="polite" unless you set a role
yourself, so a screen reader hears the placeholder when it replaces content that
has just finished loading. Set role="region" with an aria-label instead when
the block is part of the page from the start and should not announce itself.
The glyph is decorative — it is marked aria-hidden, so the heading and body
copy have to carry the meaning on their own.
The React and Svelte props, read from the components’ own types:
EmptyState
| Prop | Type | Default |
|---|---|---|
size | EmptyStateSize | 'md' |
title | ReactNode | |
iconA node replaces the default glyph; `noIcon` drops it entirely. | ReactNode | |
noIcon | boolean | false |
actions | ReactNode | |
borderedDraw a dashed placeholder box around the block. | boolean | false |
children | ReactNode |
Also accepts Omit<HTMLAttributes<HTMLDivElement>, 'title' | 'children'>.
EmptyState
| Prop | Type | Default |
|---|---|---|
size | EmptyStateSize | 'md' |
title | string | |
iconA snippet replaces the default glyph; `noIcon` drops it entirely. | Snippet | |
noIcon | boolean | false |
actions | Snippet | |
borderedDraw a dashed placeholder box around the block. | boolean | false |
children | Snippet |
Also accepts Omit<HTMLAttributes<HTMLDivElement>, 'class' | 'title'>.
The attribute names and CSS hooks below are the Web Components reference.
Properties
| Property | Attribute | Type | Default | Description |
|---|---|---|---|---|
bordered | bordered | boolean | false | Draw a dashed placeholder box around the block |
icon | icon | string | 'inbox' | Name of a bundled icon to use as the glyph |
noIcon | no-icon | boolean | false | Hide the glyph |
size | size | EmptyStateSize | 'md' | Scale of the whole block — glyph, type and padding together |
title | title | string | '' | Bold heading shown under the glyph |
Slots
| Slot | Description |
|---|---|
(default) | The explanatory body copy |
icon | Custom glyph or illustration (overrides the default icon) |
actions | Buttons that resolve the empty state |
CSS parts
| Part | Description |
|---|---|
::part(actions) | The actions area |
::part(base) | The empty-state container |
::part(description) | The body copy |
::part(icon) | The leading glyph |
::part(title) | The heading line |
CSS custom properties
| Property | Description |
|---|---|
--nana-empty-state-bg | Custom background colour |
--nana-empty-state-color | Custom text colour |
--nana-empty-state-border | Custom border colour, when bordered |
--nana-empty-state-icon-color | Custom glyph colour |
--nana-empty-state-glyph-size | Custom glyph box size, overriding size |
| Slot | Description |
|---|---|
(default) | The explanatory body copy |
icon | Custom glyph or illustration (overrides the default icon) |
actions | Buttons that resolve the empty state |
CSS Variables
Section titled “CSS Variables”| Variable | Default | Description |
|---|---|---|
--nana-empty-state-bg | transparent | Custom background colour |
--nana-empty-state-color | — | Custom text colour |
--nana-empty-state-border | — | Custom border colour, when bordered |
--nana-empty-state-icon-color | — | Custom glyph colour |
--nana-empty-state-glyph-size | 2.5rem | Custom glyph box size, overriding size |
CSS Parts
Section titled “CSS Parts”| Part | Description |
|---|---|
base | The empty-state container |
icon | The leading glyph |
title | The heading line |
description | The body copy |
actions | The actions area |