Mischief

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.

Installation

Copy the source into your project, or keep it behind a package.

npx shadcn@latest add Tinkerers-Labs/mischief-ui/empty-state
import { 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.

registry/default/empty-state/empty-state.tsx
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.