Skip to content
Components

EmptyState

A named section for an empty successful result. It shows a title with an optional description, decorative icon and caller-supplied action. Compose it inside an existing container.

Preview

No projects yet

Create a project to keep your work together.

Props

  • title
  • description
  • icon
  • action
import { IconLayoutGrid } from "@tabler/icons-react"
import { Button } from "@/components/ui/button"
import { EmptyState } from "@/components/ui/empty-state"

const createProject = () => { /* Create a project in your application. */ }

<EmptyState title="No projects yet" description="Create a project to keep your work together." icon={<IconLayoutGrid size={20} weight="regular" />} action={<Button onClick={createProject}>Create project</Button>} />

Usage

When to use

  • Explain why a successful view has no content yet and offer its first action.
  • Explain that filters returned no matches and offer a way to adjust them.

When not to use

  • Content is still loading. Use Skeleton or Spinner.
  • An operation failed. Use Alert.
  • A table has no rows. Use Table with its empty content.

Examples

First use

No projects yet

Create a project to keep your ideas and files together.
import { IconInbox } from "@tabler/icons-react"
import { Button } from "@/components/ui/button"
import { EmptyState } from "@/components/ui/empty-state"

<EmptyState title="No projects yet" description="Create a project to keep your ideas and files together." icon={<IconInbox size={20} />} action={<Button>Create project</Button>} />

Filtered results

No matching projects

Try another search or clear the filters to see all projects.
import { IconSearch } from "@tabler/icons-react"
import { EmptyState } from "@/components/ui/empty-state"
import { Link } from "@/components/ui/link"

<EmptyState title="No matching projects" description="Try another search or clear the filters to see all projects." icon={<IconSearch size={20} />} action={<Link href="#all-projects">View all projects</Link>} />

Do and don't

Do

  • Explain why there is no content and give a useful next step.
  • Use a first-use message before any content exists and a different message when filters hide content.
  • Compose inside an existing page section or Card.

Don't

  • Do not use an empty state for loading or errors.
  • Do not add a second card border around the component.
  • Do not announce static empty sections as live regions.

Props

NameTypeValuesDefaultDescription
title requirednode--Short heading naming the empty result.
descriptionnode--Explanation or next step for the empty result.
iconnode--Decorative icon supplied by the caller.
actionnode--Caller-supplied action that helps the user proceed.

Anatomy

PartDescription
rootCentered named section with token spacing and no card border.
titleHeading that names the section by default.
descriptionOptional secondary text explaining why the result is empty.
iconOptional decorative icon in the secondary icon color.
actionsCaller-supplied action such as Button or Link.

Accessibility

Role: region

  • The title names the section unless aria-label or aria-labelledby provides a different name.
  • No live region is added by default. A caller can set role="status" when results change.
  • The icon is decorative. Title and description explain the empty result.

Install

npx shadcn add https://werewolf-ui.vercel.app/r/empty-state.json

This also adds styles/werewolf.css, the tokens every component uses, and any other component it needs. Import that file once in your root CSS, after @import "tailwindcss", with a path relative to that CSS file (from app/globals.css it is @import "../styles/werewolf.css").