Wellbore Genius
Help
SimulationsDocsEmptyState

EmptyState

A reusable empty-state block for lists, tables, cards, and no-results screens. Use it instead of bare “No data” strings. Every action supports primary/secondary styling, leading or trailing icons, and TanStack Router links.

Props Playground

Tweak props live and copy the generated JSX snippet.

Primary action
Secondary action
No simulations yet
Create your first sim from the Builder.
Learn more
<EmptyState
  icon={<Inbox className="size-6" />}
  title="No simulations yet"
  description="Create your first sim from the Builder."
  actions={[
    { label: "New simulation", icon: <Plus className="size-3.5" />, onClick: handleClick },
    { label: "Learn more", iconRight: <ArrowRight className="size-3.5" />, to: "/builder" },
  ]}
/>

The URL updates as you tweak — copy and share to reproduce this exact config.

Effective configuration

Live view of the validated pg* search params after Zod parsing and safe-fallback coercion. Tamper with the URL — any invalid value snaps back to its default here.

ParamTypeEffective value
pgCompactbooleanfalse
pgDescstring"Create your first sim from the Builder."
pgIconstring"Inbox"
pgPbooleantrue
pgPIconstring"Plus"
pgPIconRstring"none"
pgPLabelstring"New simulation"
pgPLinkbooleanfalse
pgPVariantstring"auto"
pgSbooleantrue
pgSIconstring"none"
pgSIconRstring"ArrowRight"
pgSLabelstring"Learn more"
pgSLinkbooleantrue
pgSVariantstring"auto"
pgTitlestring"No simulations yet"
Basic usage

Wrap an empty screen in EmptyState. Provide title, an optional description, and one or more actions. The first action defaults to the primary button variant; subsequent actions default to outline.

<EmptyState
  icon={<Inbox className="size-6" />}
  title="No simulations yet"
  description="Create your first sim from the Builder."
  actions={[
    { label: "New simulation", icon: <Plus className="size-3.5" />, onClick: create },
    { label: "Learn more", iconRight: <ArrowRight className="size-3.5" />, to: "/docs" },
  ]}
/>
No simulations yet
Create your first sim from the Builder.
Learn more
Primary and secondary actions

Set variant on any action to override the default. The first action uses "default" (filled primary) unless you override it; every other action uses "outline". Supported variants come from the shadcn Button preset: default, secondary, outline, ghost, destructive, and link.

actions={[
  { label: "Create file", icon: <FilePlus className="size-3.5" />, onClick: create },
  { label: "Import", variant: "secondary", onClick: importFile },
  { label: "Learn more", variant: "ghost", iconRight: <ArrowRight className="size-3.5" />, to: "/docs" },
]}
No files found
Upload a file or create one from a template.
Learn more
Leading icons

Pass icon to render a leading icon before the label. Icons are automatically marked aria-hidden="true" so they do not pollute the accessible name.

actions={[
  {
    label: "Clear filters",
    icon: <RotateCcw className="size-3.5" />,
    onClick: clearFilters,
  },
]}
No matches
Try adjusting your filters to see more results.
Trailing icons

Pass iconRight for a trailing icon after the label. This is useful for CTAs that imply navigation, such as “Go to docs” or “Browse templates”.

actions={[
  {
    label: "Go to docs",
    iconRight: <ArrowRight className="size-3.5" />,
    to: "/docs",
  },
]}
Documentation
Read the full component reference.
Link actions

Use to instead of onClick to render a TanStack Link. The component renders a single <a> element (no nested button) so right-click/open-in-new-tab works naturally. The accessible name is derived from the label, ariaLabel, or title.

actions={[
  { label: "New simulation", icon: <Plus className="size-3.5" />, to: "/builder" },
  { label: "Browse templates", iconRight: <ArrowRight className="size-3.5" />, variant: "outline", to: "/templates" },
]}
Start from a template
Jump into the Builder or browse existing templates.
Compact mode

Add compact for tighter padding and smaller buttons. Use this inside panels, cards, or sidebars where the full empty-state treatment would feel heavy.

<EmptyState
  compact
  title="No alerts"
  actions={[{ label: "Add", icon: <Plus className="size-3.5" />, onClick: addAlert }]}
/>
No alerts
EmptyStateAction props
PropTypeDescription
labelReactNodeVisible button text. Used as the accessible name unless ariaLabel or title is provided.
iconReactNodeOptional leading icon (rendered before label).
iconRightReactNodeOptional trailing icon (rendered after label).
variantButton variantOverrides the default variant. First action is default; others are outline.
sizeButton sizeOptional size override. Compact mode defaults to sm.
onClick() => voidClick handler. Mutually exclusive with to.
tostringTanStack Router destination. Renders a Link. Mutually exclusive with onClick.
disabledbooleanDisables the action and sets aria-disabled="true".
titlestringTooltip / accessible-name fallback.
ariaLabelstringExplicit accessible name. Required when label is non-textual.
ariaKeyShortcutsstringOptional keyboard shortcut hint, surfaced via aria-keyshortcuts.
autoFocusbooleanFocus this action on mount. Only the first true is honored.
idstringStable key for React reconciliation and testing.
Accessibility
  • The root element has role="status", aria-live="polite", and is linked to the title and description via aria-labelledby / aria-describedby.
  • Action buttons are wrapped in a role="group" labelled “Suggested actions”.
  • Icons are aria-hidden="true"; the accessible name comes from label, ariaLabel, or title.
  • Link actions render as a single <a> (no nested button) so assistive tech reports the correct role and href.
  • Every action ships with a visible focus ring via focus-visible:ring-2.