Skip to content

Empty State

Composable placeholder content for empty views with centered and inline layout variants. Size propagates to all parts through the root's data-size attribute; variant controls root layout only.

Preview

Installation

$pnpm exec dgadd add ui/empty-state
[Installs to]src/components/ui/empty-state[Item]ui/empty-state

dgadd is not public on npm yet. Until the first release, pack @diffgazer/add from the repository and install that tarball into this app, which is what puts dgadd on pnpm exec.

UI components require Tailwind CSS v4. Local copy mode imports src/styles/styles.css; package mode uses @diffgazer/ui CSS once packages are available.

Usage

tsx
import { EmptyState, EmptyStateDescription, EmptyStateMessage } from "@/components/ui/empty-state";export default function EmptyStateDefault() {  return (    <EmptyState>      <EmptyStateMessage>No reviews found</EmptyStateMessage>      <EmptyStateDescription>Start a new review to see results here.</EmptyStateDescription>    </EmptyState>  );}

Examples

Variants

Preview

Keyboard hint

Preview

Dynamic (live)

Preview

API Reference

EmptyState

NameTypeDefaultDescription
variant"centered" | "inline""centered"Root layout. Centered stacks children vertically; inline aligns them horizontally.
size"sm" | "md" | "lg""md"Spacing and font-size scale propagated to all subparts via data-size attribute.
livebooleanfalseWhen true, adds role="status" and aria-live="polite" so screen readers announce the empty state. Keep a live EmptyState mounted across the results→empty transition (render it unconditionally, empty while results exist, and swap its children) rather than conditionally mounting it with its message already inside.
childrenReactNodeEmptyState subparts (Icon, Message, Description, Actions).

EmptyStateIcon

NameTypeDefaultDescription
childrenReactNodeVisual marker. Always rendered with aria-hidden.

EmptyStateMessage

NameTypeDefaultDescription
childrenReactNodePrimary empty-state copy.

EmptyStateDescription

NameTypeDefaultDescription
childrenReactNodeSupporting copy.

EmptyStateActions

NameTypeDefaultDescription
childrenReactNodeAction buttons or links.

EmptyStateHint

NameTypeDefaultDescription
childrenReactNodeKeyboard affordance copy, typically Kbd chips plus two or three words.

Accessibility

Notes

Layout Variants

centered variant for full-page empty states, inline variant for embedded contexts.

Size

sm for compact embedded contexts, md (default) for standard use, lg for full-page empty states. Size propagates to all parts through the root's data-size attribute.

Icon

EmptyStateIcon scales its font size with the root size, so a text glyph or a currentColor icon inherits the theme in both palettes. Avoid color emoji: they render identically in light and dark and break the monochrome identity.

Compound Composition

Compose semantic parts for icon, message, description, and actions. There is no React context here: the root stamps data-size and every part reads it through group-data variants, so a copy-mode consumer must keep the root's group/es class. Variant controls root layout only.

Keyboard Hint

EmptyStateHint is the keyboard affordance: compose it with Kbd so an empty screen ends with the key that fills it instead of a full stop. It is deliberately non-interactive — on a touch surface there is no key to press, so render EmptyStateActions (a real button) there and let the Hint be the desktop affordance; the two compose, Actions above and Hint below. Kbd is a peer composition, so copy-mode consumers who never use the hint are not forced to pull it. The inline variant lays its root out as a row, so a Message + Hint pair needs flex-col on that instance to stack.

Writing Empty States

Name what is missing, name the action, name the key. Active voice, present tense, no apology and no 'oops'. Keep the hint to about three words — under live it is announced right after the message, so a sentence there becomes a paragraph in the ear.

Accessibility

For empty states that appear dynamically (e.g., after filtering returns no results), set live on the root. This adds role="status" and aria-live="polite" so screen readers announce the change. A live EmptyState must stay mounted across the results→empty transition: render it unconditionally (empty while results exist) and swap its children, instead of conditionally mounting it already containing its message — many screen-reader/browser pairs do not announce a live region inserted with content already inside it.

Source

Install via CLI: pnpm exec dgadd add ui/empty-state.

Highlighted source loads after this disclosure opens. Browse the source repository.