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

No transactions this month

Transactions you add or import show here.

Follows the page theme and corners
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

Recent activity

No matches

Try fewer filters.

Follows the page theme and corners
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 Button that clears the filters.

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.

esc

↑ ↓ to move↵ to open