Skip to content
WingoUI
ComponentsTemplatesDocsPricingAI agentsBlogChangelogContact
Components
Buttons & Actions
ButtonButton GroupCopy ButtonDelete ButtonTheme ToggleToggleToggle Group
Inputs
Avatar UploadCalendarCheckboxColor PickerComboboxCountry SelectCron PickerCurrency InputDate PickerDate Range PickerDate Time PickerDuration PickerEmail InputEmoji PickerFile UploadFilter BuilderInputInput GroupListboxMasked InputMention InputMonth PickerMulti SelectNumber InputOTP InputPassword InputPhone InputRadio GroupRatingSearch InputSegmented ControlSelectSignature PadSliderSwitchTag InputTextareaTime PickerTimezone SelectTree Select
Forms
Address InputBooking WidgetCSV ImportFieldForm WizardInline EditInvoice line itemsRich Text EditorSchema Builder
Navigation
Back To TopBounce SidebarBreadcrumbCommand MenuGlobal SearchGooey NavHook SidebarNav MenuNav TabsPaginationProximity SidebarScroll ProgressSidebarStepsTable Of ContentsTabsTopbarUser MenuWorkspace Switcher
Overlays
Alert DialogContext MenuDialogDrawerDropdown MenuFamily DrawerHover CardPopoverShare DialogSheetShortcuts DialogTooltipTour
Feedback
AlertBannerCookie ConsentEmoji ReactionEmpty StateNotification BellNotification CenterProgressRoute ProgressSetup ChecklistSkeletonSpinnerToast
Data Display
Approval FlowAvatarBadgeCalendar ViewCode BlockComment ThreadContact cardCountdownDescription ListDiff ViewerFile ListFile managerFolderGanttJSON ViewerKbdLog ViewerMarkdownPresenceSchedulerTagTimelineTree View
Tables & Lists
Audit LogData TableDeal PipelineEditable GridKanbanListMembers tablePermissions MatrixTableTask ListVirtual List
Charts & Stats
Animated CounterArea chartBar chartDonut chartFunnel chartGaugeGitHub ActivityHeatmap calendarLeaderboardLine chartRadar chartRadial barSparklineStat CardTrackerTrend badge
Layout
AccordionApp ShellCardCollapsibleDetail LayoutEntity HeaderPage HeaderScroll AreaSeparatorSettings LayoutSplit ViewToolbar
Media
Audio PlayerCarouselCSV ViewerDOCX EditorDOCX ViewerExcel ViewerFile SystemImage compareImage CropperImage GalleryImage LightboxLocation MapPDF ViewerPowerPoint ViewerQR CodeStep PlayerVideo Player
AI Kit
Agent Status CardChat MessageChat ThreadChat WidgetConversation ListFluid OrbGrid RevealMatrix OrbModel PickerPrompt InputReasoning IndicatorRule BuilderSuggestion ChipsTool Call TimelineUsage MeterVoice InputWorkflow Canvas
Text & Effects
Background PatternGravity LettersMarqueeTerminalText AnimateText Rotate
Mobile
Action SheetBottom Tab BarFABHaptic ButtonHero HeaderInstall PromptLarge Title HeaderNotification BannerOnboarding CarouselPicker WheelPull To RefreshSlide To ConfirmSticky CTA BarSwipe DeckSwipeable Tabs
Commerce
Cart drawerCategory pickerCheckout stepsCoupon inputFavorite buttonFilter panelListing cardListing gridOrder trackerPricePrice rangePricing tableProduct galleryQuantity stepperReviewsSeller card
Marketing Sections
App Download SectionBlog Cards SectionCTA SectionFAQ SectionFeature GridFeature ShowcaseHero SectionIntegrations SectionLogo CloudMarketing FooterMarketing HeaderMarketing SectionNewsletter SectionPricing SectionStats SectionSteps SectionTeam SectionTestimonials SectionTimeline SectionVideo Section
Blocks
Admin Dashboard PageAdmin ShellAI Chat PageAPI Keys PageAutomation Builder PageCheckout PageCRM Contact PageCRM Pipeline PageError PagesInbox PageInvoice PageKPI DashboardListing Detail PageLogin PageMarketplace Home PageMarketplace Search PageMarketplace ShellMobile App HomeOnboarding FlowOrder History PagePost Listing WizardPricing PageProfile PageRegister PageSettings PageUsers Admin Page
Templates
About PageAcademy PageAccessibility PageAffiliate PageAgency PageAI Product PageAppointment Booking PageBlog PageBlog Post PageBug Bounty PageCareers PageChangelog PageCommunity PageCompare PageContact PageCustomer Story PageCustomers PageDemo Booking PageDevelopers PageDocs Home PageDownload PageEnterprise PageEvents PageExperts PageFAQ PageFeature PageGlossary PageHelp Article PageHelp Center PageHome Services PageImpact PageIntegration Detail PageIntegrations PageJob Detail PageLanding PageLaunch PageLegal PageManifesto PagePortfolio PagePress PageProduct Tour PagePromo PageReal Estate PageReferral PageResearch Report PageRestaurant PageRoadmap PageROI Calculator PageSecurity PageSolutions PageStartups PageStatus PageStorefront PageSwitch PageTemplate Gallery PageTravel PageWaitlist PageYear in Review Page
Hooks & Utilities
AI ChatChart kitCommerceDate utilsFile exportFile typesFiltersForm validationHapticsInvoicingMotion presetsNavigationNumber formatRelative timeRomanian locationsSpreadsheetText matchTonesUIProvideruseClipboarduseConsentuseControllableStateuseCooldownuseDebounceuseKeyboardInsetuseLocaleuseLongPressuseMediaControlsuseMediaQueryuseOverflowItemsusePersistedStateuseScrollEdgesuseScrollStateuseSelectionuseShortcutuseStickToBottom
GalleryTheme

Feedback

Empty State

Feedback

Empty State React component for Tailwind CSS v4

Emoji ReactionNotification Bell
Live props
Content
preset
title
description
retryLabel
Appearance
tone
color
size
variant
Layout
align
Behavior
titleAs
live
animated
Code
import { EmptyState } from "@/components/ui/empty-state"

<EmptyState />
  1. Home
  2. Components
  3. Feedback
  4. Empty State
Free

Empty State React component for Tailwind CSS v4

v1.0.0·Added in 1.0.0

A React empty state for lists, tables, searches and pages with nothing to show yet: an icon tile, a title, a description and actions.

It appears with the tile scaling up and the text and buttons rising in one after another; under reduced motion it only fades. Pick a preset to swap icon, tone and copy at once. Press the retry button on the error preset and it spins until the returned promise settles. Tab moves through the actions in reading order, Enter or Space presses them. With live on, a screen reader announces the title and description once when the state appears. On a phone, or anywhere narrower than 400px, two or more actions stack full width with the primary one on top, and lg pages drop to the md spacing below 640px.

Empty State is a free React component in the React feedback components category, written in TypeScript for React 19 and Tailwind CSS v4. It installs as source with npx wingo-ui@latest add empty-state, together with Button, Motion presets, Tones and UIProvider and the npm packages @radix-ui/react-slot, motion and lucide-react.

Add to a project

Requirements: React 19 and Tailwind CSS v4, in Next.js (App Router or Pages), Vite or React Router. Installation guide · Next.js setup · Vite setup

npx wingo-ui@latest add empty-state

Usage

Import EmptyState from @/components/ui/empty-state.

import { FileUp, UserPlus } from "lucide-react"
import { Button } from "@/components/ui/button"
import { EmptyState } from "@/components/ui/empty-state"

export function ClientsEmpty({ onReload }: { onReload: () => Promise<void> }) {
  return (
    <div className="flex flex-col gap-4">
      <EmptyState
        variant="surface"
        title="No clients yet"
        description="Add your first client or import the list from a CSV to get started."
        actions={
          <>
            <Button variant="outline" leftIcon={<FileUp />}>Import CSV</Button>
            <Button leftIcon={<UserPlus />}>Add client</Button>
          </>
        }
      />
      <EmptyState
        preset="error"
        size="sm"
        title="We could not load the invoices"
        description="Check your connection and try again."
        onRetry={onReload}
      />
    </div>
  )
}

Props

  • presetdefault "empty"
    "empty" | "no-results" | "error" | "first-run" | "no-permission" | "offline"

    Picks the default icon, tone and copy for the usual reasons: empty (Inbox, neutral), no-results (SearchX, neutral), error (CircleAlert, danger), first-run (Sparkles, brand, with a plus badge), no-permission (Lock, warning), offline (WifiOff, neutral). Every piece stays overridable.

  • titledefault ""
    ReactNode

    The heading. Leave empty for the preset's; null hides it.

  • descriptiondefault ""
    ReactNode

    One or two sentences under the title; wraps at 36ch (44ch at lg). Leave empty for the preset's; null hides it.

  • icon
    ReactNode

    The glyph inside the tile, sized by the empty state. Defaults to the preset's icon; null hides the tile.

  • illustration
    ReactNode

    Replaces the tile with an image or an svg, at most 112 / 160 / 192px tall by size. Decorative (aria-hidden).

  • badge
    ReactNode

    A small round badge on the tile's corner in the tone's fill. first-run shows a plus; null hides it.

  • tonedefault ""
    "neutral" | "brand" | "success" | "warning" | "danger" | "info"

    The tile tint. Empty uses the preset's tone. neutral is a quiet gray tile; the others use the tone's soft fill and text.

  • colordefault ""
    string

    Any CSS color for the tile (and the badge); replaces the tone.

  • sizedefault "md"
    "sm" | "md" | "lg"

    sm inside a table, list or popover (40px tile, 14px title, 24px padding); md in a panel (48px tile, 15px title, 40px padding); lg for a whole page (64px tile, 18px title, 64px padding, 40px below 640px).

  • variantdefault "plain"
    "plain" | "dashed" | "surface"

    plain sits on whatever is behind it; dashed draws a dashed frame (first-run areas, drop zones); surface is a raised panel.

  • aligndefault "center"
    "center" | "start"

    start aligns text and actions to the start edge, for cards and sidebars.

  • actions
    ReactNode

    Buttons. Put the primary action last in the DOM: it sits at the end of the row and on top when the row stacks.

  • onRetry
    () => (void | Promise<unknown>)

    Adds an outline retry button after the actions (meant for the error and offline presets). A returned promise keeps it loading until it settles.

  • retryLabeldefault "Try again"
    string

    The retry button's label.

  • titleAsdefault "h3"
    "h2" | "h3" | "h4" | "p"

    The title's element, to fit the page's heading outline.

  • livedefault false
    boolean

    role="status" with a polite live region, so a screen reader announces the state when it appears. Use it for results that change, like no-results under a search.

  • animateddefault true
    boolean

    Tile, text and actions rise in with a short stagger when the state mounts. Server-rendered markup shows the final state; reduced motion fades only. The demo's replay button remounts it.

  • presets
    Partial<Record<EmptyStatePreset, Partial<EmptyStatePresetConfig>>>

    Overrides merged over DEFAULT_EMPTY_STATE_PRESETS ({ icon, tone, title, description, badge } per preset). Translate once for the whole app with <UIProvider defaults={{ "empty-state": { presets } }}>.

  • children
    ReactNode

    Extra content between the description and the actions: suggestions, a search field, a list of shortcuts.

  • classNames
    Partial<Record<"media" | "icon" | "badge" | "title" | "description" | "content" | "actions", string>>

    Extra classes for the inner parts: the tile or illustration, the glyph, the corner badge, the title, the description, the children wrapper, the actions row.

  • className
    string

    Extra classes for the root, merged last.

Parts

  • EmptyStateIcon

    component

    The decorative icon tile on its own (data-slot empty-state-media). Inside an EmptyState it follows its size and entrance. Props: { tone?, color?, badge?, size?, classNames?: { icon, badge } } & div props.

  • EmptyStateTitle

    component

    The title part, sized by the surrounding EmptyState. Props: { as?: "h2" | "h3" | "h4" | "p" } & heading props.

  • EmptyStateDescription

    component

    The description part, sized by the surrounding EmptyState. Takes p props.

  • EmptyStateActions

    component

    The actions row (wraps, stacks full width below 400px). asChild renders your own element as the row. Props: { asChild?: boolean } & div props.

  • DEFAULT_EMPTY_STATE_PRESETS

    constant

    The English preset copy, icons and tones, to spread and translate. Type: Record<EmptyStatePreset, EmptyStatePresetConfig>.

Examples

All six presets

Each preset brings its icon, tone and copy; the demo passes its own copy through presets.

No results in a search

Type a city: the list and the empty state swap with a fade, and the clear action empties the query.

Error with retry

onRetry returns a promise, so the button spins. The first retry fails, the second loads.

First run with an illustration

dashed, lg, brand tone and a fanned photo stack in place of the tile.

Start aligned in a card

align="start" for sidebars and widgets, with a custom icon.

Inside a table

size="sm" in a full-width cell under a real header.

Sizes and a custom color

sm, md and lg side by side, tinted with color="#7C3AED".

Dependencies

@radix-ui/react-slotmotionlucide-reactbuttonmotiontonesui-config

Files

  • components/ui/empty-state.tsx

FAQ

Is the React Empty State component free?

Yes. The source is free and needs no account: copy it from the Code tab or install it with the CLI.

How do I install Empty State?

Run npx wingo-ui@latest add empty-state. It also adds the Wingo UI items it builds on (Button, Motion presets, Tones and UIProvider) and the npm packages @radix-ui/react-slot, motion and lucide-react. With shadcn, run npx shadcn@latest add https://wingo-ui.com/r/empty-state.json.

How does Empty State behave on phones?

On a phone, or anywhere narrower than 400px, two or more actions stack full width with the primary one on top, and lg pages drop to the md spacing below 640px.

See also

AlertSkeletonTableListCardButton

Used in

Marketplace Home PageMobile App HomeKanbanFile managerCart drawerListing gridData TableDeal PipelineReviewsCSV ImportInvoice line itemsSchema Builder

and 42 more.

More React feedback components

Notification BellNotification CenterSetup ChecklistSpinnerRoute ProgressEmoji Reaction
All 13 React feedback components

Articles using this component

  • React Empty State Component: First Use, No Results, Errors

    Component guides · Oct 19, 2026 · 8 min read

  • React Skeleton Loader: Real Shapes, No Layout Shift

    Component guides · Oct 19, 2026 · 9 min read

  • React 404 Page: 5 Error Screens for the Next.js App Router

    Build it: screens and apps · Oct 16, 2026 · 11 min read

PreviousEmoji ReactionNext Notification Bell
Clients0 records

No clients yet

Add your first client or import the list from a CSV to get started.

Switch the preset to swap icon, tone and copy