What a page or a table shows where it has nothing yet: what is missing, what to do, and one action.
Basic
An icon, a title, one line and one action, centered in a column. The title says what is missing in the reader's words, and the line says what to do about it. The action is the one thing that fixes it.
Example
Example
No transactions this month
Transactions you add or import show here.
import { Button, EmptyState } from '@snapshot-ui/react';
import { Receipt } from 'lucide-react';
import type { ReactElement } from 'react';
export default function EmptyStateBasic(): ReactElement {
return (
<EmptyState
icon={<Receipt />}
title="No transactions this month"
description="Transactions you add or import show here."
action={<Button variant="secondary">Import transactions</Button>}
/>
);
}In a card
An empty state is a card's empty body, or a table's. Set headingLevel to the step under the heading that holds it: under a card title at level 3, the empty state's title is level 4.
Example
Example
Recent activity
No matches
Try fewer filters.
import { Button, Card, CardBody, CardHeader, CardTitle, EmptyState } from '@snapshot-ui/react';
import { SearchX } from 'lucide-react';
import type { ReactElement } from 'react';
export default function EmptyStateInCard(): ReactElement {
return (
<Card
as="section"
aria-labelledby="empty-state-in-card-title"
style={{ maxWidth: 480, marginInline: 'auto' }}
>
<CardHeader>
<CardTitle id="empty-state-in-card-title" level={3}>
Recent activity
</CardTitle>
</CardHeader>
<CardBody>
<EmptyState
headingLevel={4}
icon={<SearchX />}
title="No matches"
description="Try fewer filters."
action={<Button variant="tertiary">Clear filters</Button>}
/>
</CardBody>
</Card>
);
}In a data table
Data table's emptyState takes one, drawn across every column of its empty body. Its title says what the table is missing, such as "No transactions this month", and its action is the one that brings rows back, such as clearing the filters.
Usage
Empty state is static. It renders in a server component and ships no client JavaScript. Pass a Lucide icon element, which the empty state draws at 24px in slate and hides from assistive technology. Give it one action at most, such as a Button that clears the filters or imports the first rows.
import { Button, EmptyState } from '@snapshot-ui/react';
import { Receipt } from 'lucide-react';
export function NoTransactions() {
return (
<EmptyState
icon={<Receipt />}
title="No transactions this month"
description="Transactions you add or import show here."
action={<Button variant="secondary">Import transactions</Button>}
/>
);
}Props
The root also takes ref, and className is merged after its own class.
action ReactNode | One action, such as a |
|---|---|
className string | |
description ReactNode | What the reader can do about it. |
headingLevel 2 | 3 | 4 | Default: 3, the step under a card's title |
icon ReactNode | A Lucide icon element, drawn at 24px in slate. |
title string | The line that says what is missing: "No transactions this month". |
Accessibility
The title is a heading at headingLevel, a level 3 by default, so the empty state fits the page's outline: pick the step under the heading that holds it. The icon is hidden from assistive technology, since the title says what it shows. The action is your own control and is named by its own text. The text is slate and ink, which pass AA on the page, a card and a floating panel.
Design rules
What keeps empty states in the system.
Say what is missing, in the reader's words.
Say what to do about it, and offer the one action that does it.
Match headingLevel to the heading that holds the empty state.
Never draw the text in taupe or with a placeholder's look. The reader needs to read it.
Never offer two actions. One clear way out is enough.
Never put an illustration in place of the words.