EmptyState
UI primitive used to represent valid empty states with clear messaging and optional actions.
Explain valid absenceLink to section
The EmptyState component represents a valid state where there is no data to display.
Use it when:
- a list has no items
- a table has no results
- a dashboard has no data yet
- filters return nothing
- a feature has not been configured
- a user has not created their first object
EmptyState explains what happened and can guide the user toward the next action.
It is a pure UI primitive: it structures content, not product or business logic.
Core idea
Empty is not failure. EmptyState explains a valid absence and helps the user move forward.
ImportLink to section
import { EmptyState } from "@pycolors/ui";Basic usageLink to section
Use EmptyState when the application is in a valid state, but there is no content yet.
No projects yet
Create your first project to get started.
import { EmptyState } from "@pycolors/ui";
export function BasicEmptyState() {
return (
<EmptyState
title="No projects yet"
description="Create your first project to get started."
/>
);
}When to use EmptyStateLink to section
Use EmptyState when the request succeeded or the current state is valid, but the result is empty.
First-run states
Use when the user has not created projects, members, records, or resources yet.
Filtered results
Use when filters, search, or query constraints return no matching data.
Product surfaces
Use in dashboards, tables, cards, panels, and feature sections.
With actionLink to section
Use an action when there is a clear next step.
No projects yet
Projects help you organize your work.
import { Button, EmptyState } from "@pycolors/ui";
export function EmptyStateWithAction() {
return (
<EmptyState
title="No projects yet"
description="Projects help you organize your work."
action={<Button>Create project</Button>}
/>
);
}Action rule
One clear action is usually enough. Empty states should guide users, not overwhelm them.
With iconLink to section
Use the icon slot for a simple decorative icon or illustration.
Nothing scheduled
Create an event to see it appear here.
import { Button, EmptyState } from "@pycolors/ui";
import { CalendarDays } from "lucide-react";
export function EmptyStateWithIcon() {
return (
<EmptyState
icon={<CalendarDays className="h-6 w-6" aria-hidden="true" />}
title="Nothing scheduled"
description="Create an event to see it appear here."
action={<Button variant="secondary">Create event</Button>}
/>
);
}Inside a CardLink to section
EmptyState composes naturally with surfaces such as Card.
No data available
Metrics will appear once data is collected.
import { Card, CardContent, EmptyState } from "@pycolors/ui";
export function CardEmptyState() {
return (
<Card>
<CardContent className="p-6">
<EmptyState
title="No data available"
description="Metrics will appear once data is collected."
/>
</CardContent>
</Card>
);
}Table empty stateLink to section
Use EmptyState inside a table when a query succeeds but returns no rows.
No resultsTry adjusting your filters. |
import {
Button,
EmptyState,
Table,
TableBody,
TableRow,
TableCell,
} from "@pycolors/ui";
export function TableEmptyState() {
return (
<Table>
<TableBody>
<TableRow className="hover:bg-transparent">
<TableCell colSpan={4} className="py-10">
<EmptyState
title="No results"
description="Try adjusting your filters."
action={
<Button size="sm">
Clear filters
</Button>
}
/>
</TableCell>
</TableRow>
</TableBody>
</Table>
);
}Usage patternsLink to section
Confirm the state is validLink to section
Use EmptyState only when the request succeeded or the absence is expected.
Explain why it is emptyLink to section
The title should name the state. The description should explain what it means.
Offer the next step when usefulLink to section
Add one action when the user can create, invite, reset filters, or configure something.
Match the surrounding surfaceLink to section
Use EmptyState inside Card, Table, or a section depending on the page structure.
Keep product logic outsideLink to section
EmptyState does not decide what happens next. The page or feature owns that behavior.
EmptyState vs Skeleton vs AlertLink to section
| Situation | Use |
|---|---|
| Data is loading | Skeleton |
| Request succeeded but there is no data | EmptyState |
| Request failed | Alert |
| User must confirm an action | Dialog |
| User needs a side workflow | Sheet |
| Onboarding requires several steps | Dedicated onboarding UI |
Decision rule
EmptyState explains valid absence. Skeleton represents loading. Alert communicates failure.
APILink to section
Props
| Prop | Type | Required | Description |
|---|---|---|---|
title | string | Yes | Primary message explaining the empty state |
description | string | No | Additional context for the empty state |
action | React.ReactNode | No | Optional action |
icon | React.ReactNode | No | Optional decorative icon or illustration |
className | string | No | Additional Tailwind classes |
EmptyState also accepts standard HTML div props.
React.HTMLAttributes<HTMLDivElement>TypeScript typesLink to section
export interface EmptyStateProps
extends React.HTMLAttributes<HTMLDivElement> {
title: string;
description?: string;
action?: React.ReactNode;
icon?: React.ReactNode;
}AccessibilityLink to section
EmptyState should be readable and understandable without relying on icons.
Guidelines:
- keep the title clear and descriptive
- keep icon-only meaning out of the icon
- mark decorative icons with
aria-hidden="true" - do not force focus when an empty state appears
- actions should be keyboard accessible
- do not use EmptyState to announce errors
- use Alert for failures and blocking problems
Accessibility rule
Empty states should explain themselves through text. Icons can support the message, but they should not carry the meaning alone.
Prefer / avoidLink to section
Prefer
- clear title and short description
- one useful action when appropriate
- different copy for first-run and filtered-empty states
- icons as supportive decoration
- consistent placement inside Card or Table surfaces
Avoid
- using EmptyState for errors
- using EmptyState while data is still loading
- vague titles like “Nothing here”
- multiple competing actions
- business logic inside the component
Product copy guidelinesLink to section
Empty state copy should answer two questions:
- Why is this empty?
- What can the user do next?
No projects yet
Create your first project to organize your work.
No results found
Try adjusting your filters or clearing the current search.
No metrics available
Metrics will appear once your app starts receiving data.