51 / Blocks
Empty State
What to show when there is nothing yet: what this place is for, and the way to fill it.
No documents yet
Upload a PDF and it will show up here, ready to split, redact, or sign.
"use client"
import { FileText, Inbox } from "lucide-react"
import { DemoVariants } from "@/components/demos/demo-variants"
import { EmptyState } from "mischief-ui/empty-state"
const button =
"border-border text-foreground hover:bg-muted min-h-9 rounded-md border px-3 text-xs"
export function EmptyStateDemo() {
return (
<DemoVariants
label="Empty state"
variants={[
{
id: "documents",
label: "With an action",
render: () => (
<EmptyState
icon={<FileText aria-hidden="true" size={18} />}
title="No documents yet"
description="Upload a PDF and it will show up here, ready to split, redact, or sign."
actions={
<button className={button} type="button">
Upload a file
</button>
}
/>
),
},
{
id: "compact",
label: "Compact",
render: () => (
<EmptyState
size="sm"
icon={<Inbox aria-hidden="true" size={16} />}
title="Nothing to review"
/>
),
},
]}
/>
)
}Installation
Copy the source into your project, or keep it behind a package.
npx shadcn@latest add Tinkerers-Labs/mischief-ui/empty-stateimport { EmptyState } from "mischief-ui/empty-state"Or paste it in yourself. The source imports the shared cn helper from @/lib/utils, so point that at your own copy.
import * as React from "react" import { cn } from "@/lib/utils" export type EmptyStateProps = Omit< React.HTMLAttributes<HTMLDivElement>, "title"> & { title: React.ReactNode description?: React.ReactNode icon?: React.ReactNode /** Controls placed under the description, such as a primary action. */ actions?: React.ReactNode size?: "sm" | "md"Usage
export function NoFiles() {
return (
<EmptyState
icon={<FileText size={18} />}
title="No documents yet"
description="Upload a PDF to get started."
actions={<button type="button">Upload</button>}
/>
)
}Writing one worth reading
An empty state is the first thing many people see, and it is usually written last. The default -- No data -- tells someone what they can already see and nothing about what to do, which turns a starting point into a dead end.
- Say what is missing in the words of the thing itself: No documents yet, not No results.
- Say why the space is empty, when the reason is not obvious: nothing uploaded yet reads very differently from a filter that matched nothing.
- Offer the one action that fills it, and only one. A choice of three is a menu, not a way forward.
Distinguish the two kinds. Nothing has ever been here is an invitation, and should show someone how to begin. Nothing matched what you asked for is a result, and should offer a way back -- clearing the filter, widening the search -- rather than the same create button.
Fitting the space
The default has room to breathe, for a page or a large panel that is otherwise blank. Use size="sm" inside a card, a sidebar, or a column where a tall empty box would push the rest of the layout around.
The description is held to a readable measure rather than stretching the full width of whatever contains it, so it stays legible in a wide panel without any work on your part.
API
titleReactNodeThe one line saying what is missing.descriptionReactNodeA sentence of context, held to a readable measure.iconReactNodePlaced in a ring above the title.actionsReactNodeControls beneath, such as the way to add the first item.size"sm" | "md"Vertical room. Use sm inside a panel.Accessibility
The icon is decoration the screen reader skips, so the title carries the meaning. The description is capped at a readable measure rather than stretching across a wide panel. Nothing here traps focus or announces itself; it is a static region, and the action inside it is your own control with your own semantics.