Components/EmptyState

EmptyState

Feedback component for empty, loading, error, offline, and setup states.

VerifiedSince 0.3.4

Usage

EmptyState (variant)

Loading demo...

Best Practices

  • Use the most specific preset before overriding copy: search-empty for filters, no-selection for split panes, permission for access control, and offline for network failures.
  • Keep title copy situational and actionable, not generic. Prefer “No automation rules yet” over “Empty”.
  • Render loading, empty, and error states inside the same data container to prevent layout jumps.
  • Use generated primaryAction / secondaryAction for standard flows; switch to the actions slot only when the action layout needs custom composition.
  • Do not show both a custom icon and loading=true; a provided icon intentionally suppresses the spinner.
  • Prefer surface="card" inside dashboards and panels; keep plain for already-contained tables, drawers, and cards.

API Reference

Props

PropTypeDefaultDescription
variantEmptyStateVariant'empty'Selects preset title, description, and illustration for common states.
titlestring-Overrides the preset title. Pass an empty string to hide generated title text when no title slot is present.
descriptionstring-Overrides the preset supporting text. Pass an empty string to hide generated description text when no description slot is present.
iconTxIconSource | string | null-Replaces the preset illustration with a Tuff icon source or icon class/name. Pass null to hide the icon area.
iconSizenumbersize presetSets custom icon or spinner size in pixels; default is 28, 36, or 44 for small/medium/large.
layout'vertical' | 'horizontal''vertical'Controls whether icon and content stack vertically or sit side by side.
align'start' | 'center' | 'end''center'Aligns icon, copy, and actions within the empty-state panel.
size'small' | 'medium' | 'large''medium'Adjusts spacing, text scale, and preset illustration size.
surface'plain' | 'card''plain'Adds a bordered card surface when the empty state needs visual containment.
primaryActionEmptyStateAction-Renders the primary action button and emits primary when clicked.
secondaryActionEmptyStateAction-Renders the secondary action button before the primary action and emits secondary when clicked.
actionSizeTxButtonProps['size']'small'Default button size used by generated actions that do not set their own size.
loadingbooleanfalseShows TxSpinner when no icon slot and no non-empty icon prop are provided.

EmptyStateVariant Defaults

VariantDefault titleDefault descriptionIllustration source
emptyNothing hereThere is nothing to show yet.Built-in SVG illustration.
blank-slateStart from scratchCreate your first item to get started.Built-in SVG illustration.
no-dataNo dataNo data available yet.Built-in SVG illustration.
no-selectionNothing selectedSelect an item to see details.Built-in SVG illustration.
search-emptyNo resultsTry a different keyword or filter.Built-in SVG illustration.
loadingLoadingPlease wait a moment.Built-in skeleton illustration, or spinner when loading=true.
offlineYou are offlineCheck your connection and retry.Built-in SVG illustration.
permissionAccess deniedYou do not have permission to view this content.Built-in SVG illustration.
errorSomething went wrongPlease try again later.Built-in SVG illustration.
guideStart hereFollow the steps to get started.Built-in SVG illustration.
customemptyemptyNone unless icon or icon slot is provided.

EmptyStateAction

PropTypeDefaultDescription
labelstring-Button text.
typeTxButtonProps['type']-Button tone shortcut forwarded to TxButton.
variantTxButtonProps['variant']-Explicit TxButton visual variant.
sizeTxButtonProps['size']-Per-action size override; falls back to actionSize.
disabledbooleanfalseDisables the generated action button; native disabled buttons do not emit the action event.
iconstring-Optional icon class forwarded to TxButton.

Slots

SlotPropsDescription
icon-Replaces preset illustration, spinner, or icon prop.
title-Replaces the resolved title content.
description-Replaces the resolved description content.
actions-Replaces generated primary and secondary action buttons.

Events

EventPayloadDescription
primary-Emitted when the generated primary action button is clicked.
secondary-Emitted when the generated secondary action button is clicked.

Horizontal Layout

EmptyState (horizontal)

Loading demo...

Custom Slots

EmptyState (slots)

Loading demo...

Dashboard Recovery States

Dashboard data regions should switch loading, empty, and error states inside one container to avoid layout jumps and duplicated placeholders.

Dashboard recovery states

A composed loading, empty, error, and compact preset state example.

Loading demo...

Preset Components

All preset components render TxEmptyState with a fixed variant and forward the same props and slots.

ComponentForced variantNotes
TxBlankSlateblank-slateDefaults to size="large", layout="vertical", and surface="plain".
TxLoadingStateloadingLoading placeholder wrapper.
TxNoSelectionno-selectionDetail panes before a list item is selected.
TxNoDatano-dataEmpty datasets after a successful load.
TxSearchEmptysearch-emptyEmpty search or filtered results.
TxOfflineStateofflineNetwork unavailable state.
TxPermissionStatepermissionAccess denied state.
TxErrorStateerrorError state; see ErrorState.
TxGuideStateguideGuided onboarding state; see GuideState.

Overview

  • Preset variant values resolve title, description, and illustration before explicit props or slots are applied.
  • Slot content wins over generated content for the same region. An actions slot replaces both generated action buttons.
  • icon=null hides the entire icon region. loading=true shows TxSpinner only when there is no icon slot and no non-empty icon prop.
  • The built-in variant="error" illustration is an app window (a title bar with three dots and two content lines) with a danger badge (a circle and an exclamation mark) on its bottom-right corner — the same object-plus-badge shape as blank-slate. The badge sits on --tx-color-danger-light-9 with --tx-color-danger ink rather than white ink on a solid danger fill. Two rings ripple out of the badge half a cycle apart (2.4s per cycle); under prefers-reduced-motion: reduce the rings stop and stay hidden, leaving the window and badge as the complete still frame.
  • Generated actions render secondary before primary; disabled generated buttons do not emit their event.
  • surface="card" adds visual containment only. It does not change semantics or button behavior.
  • Wrapper components forward props and slots to TxEmptyState; they only fix variant, except TxBlankSlate also supplies large blank-slate defaults.

Technologies

  • Reviewed against packages/tuffex/packages/components/src/empty-state/src/TxEmptyState.vue, types.ts, preset wrapper components, and empty-state tests.
  • Recommendation: use variant for data-container states; override title, description, or icon only when business context needs specific copy.
  • Accessibility: titles should explain the current user situation, and action labels should describe the next step. Avoid vague “empty” copy.
  • Motion fallback: under prefers-reduced-motion: reduce every illustration stops on a complete still frame (added for all variants 2026-09-24). Loops rest on their resting pose; the parts the animation itself draws or places — the no-data line with its end dot and cross marks, the offline slash, the search bubble — are set to the frame their animation ends on, since their resting styles are not a finished frame (a full dash offset or opacity 0 hides them; the bubble sits unshifted over the magnifier); particles that only exist in motion (the box dust, the error rings) stay hidden. Before 2026-09-23 the error illustration was a triangle with an exclamation mark plus two __error-pulse circles that had no styles at all, so they drew as two static rings straight through the triangle.
  • Verified coverage: variant defaults, layout classes, explicit title/description overrides, slot replacement, generated action events, disabled action suppression, spinner precedence, the error illustration's structure (window, danger badge, two staggered ripple rings), a reduced-motion stop for every illustration animation (a source guard that fails when a new animation: tx-empty-state-* has none) with the drawn parts resting on their final frame, icon=null, and SSR wrapper slot forwarding.
  • Component source: packages/tuffex/packages/components/src/empty-state/src/TxEmptyState.vue.
  • Types: packages/tuffex/packages/components/src/empty-state/src/types.ts.
  • Preset wrappers: packages/tuffex/packages/components/src/{blank-slate,loading-state,no-selection,no-data,search-empty,offline-state,permission-state,error-state,guide-state}/src/*.vue.
  • Coverage: packages/tuffex/packages/components/src/empty-state/__tests__/empty-state.test.ts and empty-state-wrappers.test.ts.
查看源码
packages/tuffex/packages/components/src/empty-state/index.ts