EmptyState

Placeholder for empty data — title, description, icon, and action slot.

moleculesSource
import { EmptyState } from '@elirobinson/react/components/molecules/EmptyState';

Styles: @elirobinson/react/styles/molecules/EmptyState.css — already included when you import @elirobinson/react/styles.css.

No coaching guides yet

Guides you publish will show up here, ready to share with your team.

Show code
import { EmptyState } from '@elirobinson/react/components/molecules/EmptyState';

export default function Basic() {
  return (
    <EmptyState
      title="No coaching guides yet"
      description="Guides you publish will show up here, ready to share with your team."
    />
  );
}

When to use it

Use EmptyState wherever a list, table, or search result would normally render rows and currently has none — a fresh account with no data yet, or a filtered view that matched nothing. It's Table's own empty-rows branch under the hood.

It's not a loading indicator — while data is in flight, use Skeleton. And it's not a system message about something that went wrong — an error belongs in Alert.

With icon and action

icon and action are both optional. An action gives the reader a way out of the empty state — clear a filter, create the first item — instead of leaving them at a dead end.

No recipes found

Try a different ingredient, or clear your filters and start over.

Show code
import { Button } from '@elirobinson/react/components/atoms/Button';
import { EmptyState } from '@elirobinson/react/components/molecules/EmptyState';

function SearchIcon() {
  return (
    <svg
      width="28"
      height="28"
      viewBox="0 0 24 24"
      fill="none"
      stroke="currentColor"
      strokeWidth="1.5"
      strokeLinecap="round"
      strokeLinejoin="round"
      aria-hidden="true"
    >
      <circle cx="11" cy="11" r="7" />
      <path d="m20 20-3.5-3.5" />
    </svg>
  );
}

export default function WithAction() {
  return (
    <EmptyState
      icon={<SearchIcon />}
      title="No recipes found"
      description="Try a different ingredient, or clear your filters and start over."
      action={<Button onClick={() => alert('Filters cleared.')}>Clear filters</Button>}
    />
  );
}

Props

PropTypeDefaultDescription
actionReactNode
descriptionstring
iconReactNode
titlestring

Also accepts all HTMLAttributes<HTMLDivElement> props.

Accessibility

  • Renders a plain <div> with no ARIA role — it's a content placeholder, not a live region or an alert.
  • title renders as a <p>, not a heading element. If the empty state is the main content of a section that needs a heading in the outline, add your own heading alongside it rather than relying on title.
  • icon renders exactly what you pass, with no automatic aria-hidden — mark decorative icons aria-hidden="true" yourself, the way the demo above does.
  • action is a plain slot — usually a Button — and keeps whatever accessible name and keyboard behavior that control already has.
  • The ref forwards to the outer <div>.

Do

  • Say what happened and, when there's a way forward, give an action for it — "No results — clear filters."
  • Match the tone to the situation: a fresh empty list reads differently than a filtered-to-zero one.
  • Mark decorative icons aria-hidden yourself.
  • Reuse the same EmptyState across a table and its windowed VirtualTable sibling — the placeholder logic is identical.

Don't

  • Use EmptyState for a loading placeholder — reach for Skeleton while data is in flight.
  • Use EmptyState to report an error — that belongs in Alert, with recovery guidance.
  • Leave title blank and lean on description alone; title is the one line every empty state needs.
  • Bury the action below the fold in a tall empty region — keep it close to the message.