Skip to content

EmptyState

Status: stableAccessibility reviewedtenet-ui@0.4.0
Import
import { EmptyState } from 'tenet-ui';

Also accepts every prop of Omit<HTMLAttributes<HTMLDivElement>, 'title'>.

Source: src/components/EmptyState/EmptyState.tsx

When to use: a table, list, inbox, search or filter that has nothing to show — first run (“No stories yet”), an exhausted filter (“No results for ‘budget’”), a cleared queue (“You’re all caught up”). For zero records, render it in place of the rows, inside the same Card/Table.Container, so the layout doesn’t jump. For an exhausted search or filter, keep the table and its toolbar on screen and pass a compact EmptyState (headingLevel={4}, no size="large") as DataTable emptyText. It is not for errors (use Banner variant="danger") or for loading (use Skeleton/Spinner).

  • title is required and specific: name what is empty and, for filters, echo the query. Avoid “Nothing here” / “Oops”.
  • description explains why and what to do next in one or two sentences; keep it inline content (it renders inside a paragraph).
  • action is the single step that fixes the emptiness — a Button variant="primary" (“Create story”, “Clear filters”). secondaryAction is optional and quieter (Button variant="invisible", a Link to docs). Never two primaries; no action at all is fine when there is nothing the user can do.
  • icon is decorative and is wrapped aria-hidden — the title already carries the meaning. Pass an Icon or an inline <svg> that uses currentColor; it is tinted fgColor-muted and sized from the size scale. Don’t put text or a status meaning in it.
  • headingLevel (2–4, default 3) sets the title’s rank in the outline so it nests under the surrounding page/section heading; its visual size follows size. Use size="large" only for whole-page or first-run states.
  • Don’t hand-build one: a centered <div> with inline textAlign and a <h3 style> skips the type scale, the muted tone and the outline rules this component encodes.
  • <EmptyState title="No stories yet" description="Stories you create or are assigned will appear here." action={<Button variant="primary">Create story</Button>} />
  • <EmptyState title={No results for “${query}”} description="Try a different spelling or clear the filters." action={<Button onClick={clear}>Clear filters</Button>} headingLevel={4} /> passed as DataTable emptyText, so the toolbar and active filters stay.
  • <EmptyState size="large" headingLevel={2} icon={<InboxIcon />} title="You're all caught up" /> as a page’s only content under its h1.
  • <div style={{ textAlign: 'center', padding: 40 }}><h3>Nothing here</h3></div> — off-system and unspecific.
  • <EmptyState title="Error" description="Request failed" /> — failures are a Banner, not an empty state.

See also: DataTable (emptyText for an exhausted filter), Banner for failures, Skeleton while loading; system guidelines data-display (when a collection shows an EmptyState) and content (title, description, and action copy).

6 stories for EmptyState — open in Storybook

The full matrix (all args, controls, accessibility panel) is in Storybook.

PropTypeDefaultDescription
title *stringShort, specific headline — say what is empty ("No stories yet"), not just "Nothing here".
descriptionReactNodeOne or two sentences: why it is empty and what to do next. Inline content (rendered in a muted Text).
iconReactNodeDecorative illustration or icon; wrapped aria-hidden — the title carries the meaning.
actionReactNodeThe primary next step, usually a Button variant="primary".
secondaryActionReactNodeAn optional secondary step next to action (a default/invisible Button or a Link).
size'medium' | 'large'
EmptyStateSize
mediumOverall scale: medium for a region inside a page, large for a whole-page/first-run state.
headingLevel2 | 3 | 4
EmptyStateHeadingLevel
3Semantic heading rank of the title (h2/h3/h4) so it fits the surrounding outline. The visual size follows size, not this prop.

* required