Pro

Empty state

The message for an empty list or view: a clean isometric line drawing with crisp corners (a stack, a box, a folder, a tray, a magnifier, a calendar or an unplugged cord), drawn once as it comes into view, with a title, one sentence and one action.

Page

No dinners on the calendar

Pick a night and a host. Guests RSVP from the invitation link.

Two ways to fill it

No guests yet

Import last year’s list, or add people one at a time.

The drawings

stack: a list or table

box: nothing added yet

folder: files and imports

tray: replies and messages

search: no results

calendar: nothing scheduled

unplugged: it didn’t load

Two actions in a narrow panel stack

Menu

No courses yet

Start from a past dinner or write the menu here.

Compact, inside a panel

Guests

No RSVPs yet

Replies to example.com/d/friday-supper show up here as they come in.

No results

No books match “Piranesi”

Try the author’s name, or check the spelling.

Installation

With UI Polish connected, ask your agent:

Add the UI Polish empty state to this project.

Or with the shadcn CLI:

npx shadcn@latest add https://uipolish.com/r/empty-state.json
Brings inIcons

What a list, table or view shows when there's nothing in it yet: a clean isometric line drawing of where things would be (a stack of cards, an open box, a folder, a tray, a magnifier, a calendar, an unplugged cord), a title saying what would be here, one sentence on what fills it, and the one action that does. The drawing draws itself once, the first time it comes into view. No box or tint around it, and no icon on a rounded tile: it sits centered in the space the content would take.

TSX
<EmptyState  art="calendar"  title="No dinners on the calendar"  description="Pick a night and a host. Guests RSVP from the invite link."  action={<Button><Icon name="plus" />Plan a dinner</Button>}  size="page"/>

When to use it

  • A list, table, inbox or panel with no items.
  • A search or filter with no results (say what was searched, offer a reset).
  • A full-view error: what broke, one line, and a retry.

When not to

  • Loading. Show nothing under 300ms, then a shimmering label that names the work (sweep from text) in the space the content will take.
  • A section that will always be empty for this user: hide it.

Props

PropWhat it does
titleWhat would be here: "No dinners on the calendar". Required.
descriptionOne sentence on what fills this space, or how to.
artWhich drawing: "stack", "box", "folder", "tray", "search", "calendar" or "unplugged". Leave it out for text alone.
iconDeprecated: use art. Kept so older code still renders; an icon name picks the drawing that fits it (search the magnifier, alert and warning the unplugged cord, calendar the calendar, file the folder, check the tray, anything else the stack). No icon is drawn.
actionThe next step: one Button. Optional.
secondaryActionA second way to fill the space, one step quieter than action: "Add by hand" beside "Import a file". Optional.
size"compact" (default) inside a panel or list; "page" for a whole view.
titleAsHeading level. Defaults to h3 (compact) or h2 (page).

Look

CompactPage
Drawing196 × 144264 × 194
Title--ui-text-md, 500--ui-text-2xl, 600
Sentence--ui-text-md, muted, at most 36chat most 40ch
Padding32px top and bottom64px
  • The drawing. An isometric drawing on the 2:1 pixel grid (one unit across, half a unit down), so its straight edges stay clean at 1x and 2x. It's the focal point, so it's big: 196px wide in a panel, 264px on a page (never wider than its space). Pick the one that names the space:
artDrawingFor
stackThree cards in a stack, a list of checkboxes and lines on the top oneLists, tables, runs
boxAn open, empty box, its four flaps folded outNothing added yet: a first project, a collection
folderA folder standing open, a sheet with lines insideFiles, documents, imports, invoices
trayA shallow tray with a letter dropping into itInboxes, replies, "all caught up"
searchA magnifier lying on a card of resultsNo results for a search or filter
calendarA spiral-bound desk calendar, a grid of days, one picked outNothing scheduled
unpluggedA plug lifted out of its socket, its cord curling awaySomething didn't load: a failed fetch, a lost connection
  • No slab. A tinted or bordered box around an empty state reads as a thing in itself. Without one, the empty space is the content area, waiting.
  • The drawing's size is fixed before it draws, so drawing never moves the text below.
  • Drawing to text 16px (24px on a page); title to sentence 4px; text to action 16px (24px).

One action or two

  • One: centered under the text, at its own width.
  • Two: side by side, action first, 8px apart, the pair centered. When they don't fit on one line (a side panel, a phone), they stack, action on top, and both take the full width of the space, so the pair reads as one even block rather than two ragged pills. Side by side, neither stretches.
  • The second is always one step quieter, so the eye lands on one: a primary with a secondary, or a secondary with a ghost. Same size for both.
  • Two only when both are real ways to fill the space ("Import a file", "Add by hand"). A "Learn more" link belongs in the sentence, not beside the button.
TSX
<EmptyState  size="page"  art="folder"  title="No guests yet"  description="Import last year’s list, or add people one at a time."  action={<Button><Icon name="upload" />Import a file</Button>}  secondaryAction={<Button variant="secondary">Add by hand</Button>}/>

Writing it

  • Title: what would be here, plainly. "No dinners on the calendar", "No RSVPs yet". Not "Oops!" and not "No data".
  • Sentence: what fills it, or how. One sentence.
  • Action: one, the verb that fills it ("Plan a dinner", "Copy invite link"). For no results, "Clear search" or "Clear filters". At most a second, quieter one, when there are two real ways to fill it.
  • No results: put the query in the title ("No books match “Piranesi”") and suggest what else to try.

Motion

The drawing draws once, the first time a quarter of it is on screen (one IntersectionObserver, disconnected after it fires). Each part waits half an instant step (--ui-dur-instant / 2) after the one before:

WhatDurationEasing
Each face's edge, back to front--ui-dur-base--ui-ease-draw
Each face's fill, as its edge draws--ui-dur-fast--ui-ease-enter
A cord, as one stroke--ui-dur-base--ui-ease-draw
Pills and marks on a card--ui-dur-instant--ui-ease-color

The undrawn starting pose only applies under @media (scripting: enabled), so without JavaScript the sketch is already drawn. Under prefers-reduced-motion: reduce it's drawn from the start. The button carries its own press.

Accessibility

  • The title is a real heading, so the empty state is reachable by heading navigation. Pick the level that fits the page outline.
  • The drawing is aria-hidden; the title says it all.
  • Under forced-colors: active the drawing keeps its lines in the system text color, with faces in the canvas color.
  • When the state appears because of a search, announce the result count through your search's live region, not by focusing the empty state.

Before / after

Before:

TSX
<div className="empty">No data</div>

After:

TSX
<EmptyState  art="search"  title="No books match “Piranesi”"  description="Try the author’s name, or check the spelling."  action={<Button variant="ghost" onClick={clear}>Clear search</Button>}/>