Components

Empty

A composable placeholder for empty states — no data, no results, or first-run views — with slots for an icon, title, description, and a call to action.

Basic usage

Compose EmptyHeader, EmptyMedia, and EmptyContent to guide the user toward a next action.

No messages yet

Icon only

The simplest form — an icon, title, and description with no action. Use EmptyMedia variant="icon" for the compact badge treatment.

No projects

No results

For search and filter results, use variant="default" on the media to render larger artwork and offer a reset action.

No results found

EmptyState — the composed shortcut

Nearly every empty state is the same four things: icon, title, description, action. EmptyState wraps the primitives above into a single prop-driven component so “nothing here yet” looks identical across every app in the ecosystem. Reach for the primitives only when you need a layout this does not cover. It renders no client-side state, so it works directly inside a React Server Component.

No messages yet

Props

The Empty primitives. Every part also forwards its native element props, and the emptyMediaVariants cva helper is exported for reusing the badge treatment elsewhere.

PropTypeDefaultDescription
EmptydivRoot container. Centers its children vertically and horizontally. It sets border-dashed but no border width, so add border yourself when you want the dashed outline to show.
EmptyHeaderdivGroups the media, title, and description in a max-w-sm centered column.
EmptyMediadivHolds the icon or illustration. Accepts a variant prop.
EmptyMedia.variant'default' | 'icon''default''icon' renders a 40px muted rounded badge that sizes an unsized child SVG to 24px; 'default' is an unstyled wrapper for larger artwork.
EmptyTitledivThe primary headline summarizing the empty state.
EmptyDescriptiondivSupporting text. Anchor children are auto-styled as underlined links.
EmptyContentdivSlot for the call-to-action(s) — buttons, inputs, or links — below the header. Stacks vertically; add flex-row for a button pair.
classNamestringAdditional Tailwind classes; available on every part.

EmptyState — the composed version.

PropTypeDefaultDescription
titlerequiredReact.ReactNodeThe headline. The only required prop.
descriptionReact.ReactNodeSupporting text below the title.
iconIconComponentThe icon component itself (Inbox, not <Inbox />), rendered inside the muted badge. Passing a component rather than an element is deliberate: a Lucide icon is a forwardRef object, so an element and a component cannot be told apart at runtime.
mediaReact.ReactNodeAn arbitrary node — an illustration or image — used instead of icon. Takes precedence when both are given.
actionReact.ReactNodePrimary and secondary buttons, laid out in a centered row below the description.
borderedbooleanfalseAdds the dashed outline. Use it when the empty state sits inside a table or list frame.
size'sm' | 'md''md''sm' tightens padding, shrinks the icon badge to 36px and the title to text-base — for an empty region inside a small card. 'md' is the full-page treatment.
...restReact.ComponentProps<'div'> (minus title)Forwarded to the underlying Empty root.

Accessibility

  • Decorative icons inside EmptyMedia convey no meaning on their own — the EmptyTitle and EmptyDescription text carries the message for screen readers.
  • Always pair the empty state with a clear, actionable EmptyTitle so users understand why the area is empty, not just that it is.
  • Links placed inside EmptyDescription are automatically underlined for distinguishability beyond color alone.
  • Action buttons in EmptyContent remain fully keyboard-focusable and inherit the brand focus ring.
  • EmptyState renders its icon with aria-hidden="true" for you. When you pass media instead, hide it yourself — it is decoration, and the title already carries the message.