Passer au contenu principal

EmptyState

Implemented

Empty/zero-state placeholder — icon slot + required title + optional description and action node. default/compact/centered/info variants. The fleet's most-consumed molecule (lists, tables, search results).

Categorymolecules
Sourcepatterns/molecules/emptystate.html
AI HintReach for it before hand-rolling a 'no data' row — it owns status semantics, variants and spacing. Ships no strings: localize title/description/action via the consumer. Compose into ResponsiveTable via its emptyState slot (TopEventsWidget pattern).

Zero-state placeholder — decorative icon slot + required title + description + action node. Composes the Heading and Body atoms; ships no strings of its own.

EmptyState

Feedback molecule (Family 4 — Tier 1)

Renders a `role="status"` live region with a semantic heading (h2, or h3 in compact), an optional decorative icon, an optional description, and an action slot. Three layout variants: default / compact / centered.

No results

Try a different search term or clear your filters.

No tickets yet

Your purchased tickets will appear here.

No entries

This component supports light and dark mode.

Usage

The minimum is a localized title — never render the molecule bare (NASA Rule 7). Inside tables use variant="compact"; for hero-level empty pages use variant="centered" with an action.

import { EmptyState, Button } from '@epasslive/design-system'

// Minimum — localized title only
<EmptyState title={t('noEventsTitle')} />

// Full — icon + description + action, centered
<EmptyState
  variant="centered"
  icon={<CalendarIcon />}
  title={t('noEventsTitle')}
  description={t('noEventsDefault')}
  action={<Button variant="secondary" onClick={resetFilters}>{t('clearFilters')}</Button>}
/>

// Table cells — compact via ResponsiveTable's emptyState slot
<ResponsiveTable
  emptyState={<EmptyState variant="compact" title={t('empty')} />}
  columns={columns}
  rows={rows}
/>

Props

Prop
Type
Default
Description
title*
string
—

Heading text (Heading atom — h2, or h3 in `compact`). The only required prop; also provides the accessible name via `aria-labelledby`. Never render an empty empty state (NASA Rule 7).

icon
React.ReactNode
—

Decorative icon rendered before the title. The wrapper span is `aria-hidden="true"` — never place a labelled live element (e.g. a Spinner with `aria-label`) in this slot; its label would be suppressed.

description
string
—

Secondary text (Body atom — `sm` in compact, `base` otherwise), wired via `aria-describedby` and clamped to `max-w-prose`.

action
React.ReactNode
—

Action node (e.g. a Button) rendered in a `flex flex-wrap gap-2` row. Supply any interactive element — its accessible name and tap target are owned by that element.

variant
'default' | 'compact' | 'centered'
default

Layout variant. `default`: left-aligned, `py-12`, h2. `compact`: left, `py-6`, h3, sm body — table cells and tight cards. `centered`: centered, `py-16`, h2.

ariaLabel
string
—

Deprecated — currently a no-op. The container always renders `aria-labelledby` pointing at the title id, and `aria-labelledby` takes ARIA precedence over `aria-label`, so this value never reaches assistive tech. Source fix queued; rely on `title` until it lands.

className
string
—

Additional CSS classes merged onto the container after the variant classes (e.g. `py-8` to tighten padding inside a table cell).

id
string
—

Root element id. Falls back to a generated `useId`; also derives the `-title` / `-description` aria ids.

Props

Preview

EmptyState

Prop
Type
Required
Default
Description
title
string
Required
—

Heading text — required; never render an empty empty state.

icon
React.ReactNode
Optional
—

Decorative icon node (aria-hidden by the molecule — never place a labelled live element here).

description
string
Optional
—

Secondary/descriptive text.

action
React.ReactNode
Optional
—

Action node rendered at the end (e.g. a Button).

variant
EmptyStateVariant
Optional
'default'

Layout variant: 'default' | 'compact' | 'centered' | 'info'.

ariaLabel
string
Optional
—

Currently a no-op — aria-labelledby takes precedence in the markup; a precedence fix is tracked for step 6.

className
string
Optional
—

Additional CSS classes on the status wrapper.

id
string
Optional
—

Stable root id; keys the title element for aria-labelledby.

Quick use:<EmptyState />

Accessibility

ARIA rolestatus

Type

1 rule

NASA Rule 7

title required — never render an empty empty state

ARIA

1 rule

WCAG 4.1.3

Wrapper has role='status' aria-live='polite'; CTA must be focusable

Usage

Import

tsxSnippet
import { EmptyState } from "@epasslive/design-system"

Example 1

tsxSnippet
<EmptyState title={t("empty")} description={t("emptyHint")} variant="compact" />

Example 2

tsxSnippet
<EmptyState variant="centered" title={t("noResults")} icon={searchIcon} action={<Button variant="secondary">{t("reset")}</Button>} />

Anti-patterns

— Do not render a bare <EmptyState /> — title is required

— Do not place a labelled live element (e.g. Spinner) in the icon slot — it is aria-hidden

Design System v4.1.0 | ePassLive