Empty
A zero-data placeholder — icon, title, description, and actions, with intent tints and a plain, card, or dashed container.
- Status
- Since
0.1.0- Accessibility pattern
- heading + supporting content
Last updated
No messages yet
Your inbox is empty. New messages will appear here as they arrive.
Install
Add Empty from the VegaStack registry. The CLI verifies the item's integrity hash before writing it.
pnpm dlx shadcn@latest add @vegastack/emptyUsage
import {
Empty,
EmptyContent,
EmptyDescription,
EmptyMedia,
EmptyTitle,
} from "@/components/ui/empty";
import { Inbox } from "lucide-react";
<Empty variant="dashed">
<EmptyHeader>
<EmptyMedia>
<Inbox />
</EmptyMedia>
<EmptyTitle>No messages</EmptyTitle>
<EmptyDescription>Your inbox is empty.</EmptyDescription>
</EmptyHeader>
</Empty>;Anatomy
Empty is a compound component. Compose the parts inside the root:
<Empty variant="dashed" size="md">
<EmptyHeader>
<EmptyMedia intent="default">{/* lucide icon */}</EmptyMedia>
<EmptyTitle>Title</EmptyTitle>
<EmptyDescription>Supporting description text.</EmptyDescription>
</EmptyHeader>
<EmptyContent>{/* action buttons */}</EmptyContent>
</Empty>Empty— the centered container (data-slot="empty"). Ownssizeandvariant.EmptyMedia— a tinted circular chip around alucide-reacticon (data-slot="empty-media", decorativearia-hidden). Owns theintenttint.EmptyTitle— the primary heading (<h3>by default,data-slot="empty-title"). Ownsas, so the page picks the heading level.EmptyDescription— supporting body text (<p>,data-slot="empty-description").EmptyContent— a centered row of call-to-action controls (data-slot="empty-content").
Examples
Sizes
size controls vertical density only — sm (py-8) for empties inside cards, md (py-12)
standalone, and lg (py-16) for full-page zero-data screens.
No messages yet
Density sm — vertical padding scales from compact (in-card) to full-page.
No messages yet
Density md — vertical padding scales from compact (in-card) to full-page.
No messages yet
Density lg — vertical padding scales from compact (in-card) to full-page.
Variants
variant is the one container axis. plain (the default) has no border and no fill, so the block
blends into the surrounding layout; dashed draws the classic drop-zone outline; card paints a
filled bg-card panel with the one hairline, which reads as a self-contained block on any
background. There is no way to ask for a dashed card — that combination had no defined outcome.
Dashed
The drop-zone outline over the parent background — no fill.
Card
Filled bg-card panel — a self-contained block that reads on any background.
No messages yet
The borderless default — no dashed outline, blends into the surrounding layout.
Without an icon
EmptyMedia and EmptyContent are both optional. A title-and-description-only empty is
a common compact form — for example, a "no filters applied" hint.
No filters applied
A compact title-and-description empty with no icon chip and no actions.
Variant × size matrix
The two root axes combine freely. This grid crosses variant (dashed vs. card) with size
(sm vs. lg) to show how padding, panel fill, and the dashed outline interact.
dashed · sm
variant=dashed × size=sm
dashed · lg
variant=dashed × size=lg
card · sm
variant=card × size=sm
card · lg
variant=card × size=lg
Intents
EmptyMedia takes an intent that tints the chip — neutral default, info, or
destructive — each mapping to semantic status tokens (bg-X-subtle text-X-text). Use
destructive for error/failure empties, info for search/filter empties, and default for plain
zero-data states.
No members yet
Invite teammates to start collaborating.
No results found
Try adjusting your search or filters.
Couldn’t load records
The request timed out. Check your connection and try again.
Illustration & value tiers
The empty-state system spans eight tiers; two are Wave-2 additions. EmptyIllustration
renders one of six built-in monoline drawings (clipboard, bell, search, box, error,
not-found) on a faint grid-paper ground — all currentColor, so the error tier is just
text-destructive-text on the media slot, no extra asset. EmptyValue is the inline tier: a
single faint phrase for a value slot ("No value", "Set a value…").
No tasks yet
Create your first task to get started.
No mailboxes configured
Configure a mailbox to unlock sending.
No results
Try a different query.
Playground
Combine the container variant and size with the media chip's variant and intent, then copy the generated JSX.
No messages
Your inbox is empty.
<Empty>
<EmptyHeader>
<EmptyMedia>
<Inbox />
</EmptyMedia>
<EmptyTitle>No messages</EmptyTitle>
<EmptyDescription>Your inbox is empty.</EmptyDescription>
</EmptyHeader>
</Empty>API Reference
Empty
| Prop | Type | Default | Description |
|---|---|---|---|
size | "lg" | "md" | "sm" | "md" | Vertical density — sm for inside cards, md standalone, lg for
full-page empties. |
variant | EmptyVariant | "plain" | Container treatment.
- plain: no border, no fill — blends into the parent surface (default).
- card: a filled bg-card panel with the one hairline.
- dashed: the classic dashed drop-zone outline. |
Data attributes and CSS variables on Empty
| Attribute | Values |
|---|---|
data-size | mirrors a prop or state value |
data-slot | "empty" |
data-variant | mirrors a prop or state value |
EmptyMedia
| Prop | Type | Default | Description |
|---|---|---|---|
intent | EmptyIntent | "default" | Color intent of the icon chip — neutral default, info, or destructive. |
variant | "default" | "icon" | "icon" | icon wraps children in the tinted circular chip; default renders them
bare (illustration, avatar, screenshot). |
Data attributes and CSS variables on EmptyMedia
| Attribute | Values |
|---|---|
data-intent | mirrors a prop or state value |
data-slot | "empty-media" |
data-variant | mirrors a prop or state value |
EmptyTitle
| Prop | Type | Default | Description |
|---|---|---|---|
as | EmptyTitleAs | 'h3' | The element to render. The default <h3> is a GUESS about the host page's heading outline:
an empty state under an <h2> section wants h3, one that replaces a whole page body may
want h2, and one inside a card that already has its own heading should not introduce a
heading at all (p). Say which rather than inheriting the guess (B7-07).
This is an as union rather than Base UI's polymorphic render, because useRender is a
hook and Empty is server-safe — the whole file has no 'use client' and must keep it. |
Data attributes and CSS variables on EmptyTitle
| Attribute | Values |
|---|---|
data-slot | "empty-title" |
EmptyDescription
EmptyDescription adds no props of its own — it accepts everything the underlying element or Base UI primitive accepts (className, ref, ARIA attributes, event handlers).
Data attributes and CSS variables on EmptyDescription
| Attribute | Values |
|---|---|
data-slot | "empty-description" |
EmptyContent
EmptyContent adds no props of its own — it accepts everything the underlying element or Base UI primitive accepts (className, ref, ARIA attributes, event handlers).
Data attributes and CSS variables on EmptyContent
| Attribute | Values |
|---|---|
data-slot | "empty-content" |
Accessibility
- The title renders as a real heading (
<h3>by default) so assistive technology can navigate to it. Setasto match the page's outline —h2when the empty state replaces a whole page body,pwhen the surrounding card already carries the heading. - The icon chip is decorative (
aria-hidden) — meaning is carried by the title and description text, never by the icon or color alone. - Action controls inside
EmptyContentare real buttons/links with their own accessible names and:focus-visiblerings. - The container is non-interactive; keyboard focus order flows through the action controls only.
| Key | Action |
|---|---|
| Tab | Move focus to the first action control (when actions are present). |
| Enter / Space | Activate the focused action control. |
| Contract | States tested |
|---|---|
| Behaviour | default, empty, error |
| Accessibility | native-or-base-ui-semantics, browser-accessibility-test |
| Visual | default, error, empty |