Preview
Empty and error states pattern
Give every non-happy path a clear explanation, a next action, and a status that does not rely on color alone.
No projects yet
Create the first project to give your team a place to start.
Tabs showing empty, no-results, loading, and error state compositions
"use client";
import { Button } from "@phuctech/ui/components/button";
import { Card, CardContent, CardTitle } from "@phuctech/ui/components/card";
export function EmptyState() {
return (
<Card>
<CardContent className="grid justify-items-center gap-3 p-8 text-center">
<CardTitle>No projects yet</CardTitle>
<p className="text-sm text-muted-foreground">Create the first project to get started.</p>
<Button type="button">Create project</Button>
</CardContent>
</Card>
);
}State model
Copy
Write from the person’s point of view. Name the object, explain the condition in plain language, and use an action that says what will happen next. Avoid vague “Something went wrong” copy and avoid apologies that do not help recovery.
"use client";
import { Button } from "@phuctech/ui/components/button";
import { Card, CardContent, CardTitle } from "@phuctech/ui/components/card";
export function EmptyState() {
return (
<Card>
<CardContent className="grid justify-items-center gap-3 p-8 text-center">
<CardTitle>No projects yet</CardTitle>
<p className="text-sm text-muted-foreground">Create the first project to get started.</p>
<Button type="button">Create project</Button>
</CardContent>
</Card>
);
}- Empty: “No projects yet” plus “Create project.”
- No results: repeat the query or filter context and offer “Clear search.”
- Error: say what could not load, preserve safe context, and offer “Try again.”
Accessibility
- Keep the state heading in the document structure; do not communicate the state with an illustration or color alone.
- Use
role="status"for non-urgent loading/success updates androle="alert"only for an error that needs immediate attention. - Make Retry and the recovery action keyboard-focusable and keep the label specific.
- Respect reduced motion. A spinner may be decorative, but the loading message must still be available as text.
Do and don’t
Treat emptiness as an invitation to act and errors as a short path back to a trusted state.
Show a blank container, hide the recovery action behind hover, or make the user guess whether data was deleted.
Related
- Button gives the recovery action a clear verb and loading state.
- Card keeps a state message and its action together.
- Accessibility documents the shared focus and contrast contract.