# Empty State

> Empty state with icon, copy and actions, optionally dashed, for empty lists, searches and upload areas.

- Author: Atelier UI
- License: MIT
- Page: https://chromotion.dev/en/components/empty-state
- Site: Atelier UI (https://chromotion.dev)

## Install

```bash
npx shadcn@latest add https://chromotion.dev/r/empty-state.json
```

Stack: React + TypeScript + Tailwind CSS v4, shadcn/ui conventions. `cn` comes from `@/lib/utils`.

## Dependencies

```bash
npm install lucide-react
```

## Usage

```tsx title="components/empty-state-demo.tsx"
"use client"

import { FileSearch, Inbox, Plus, Upload } from "lucide-react"

import { EmptyState } from "@/components/empty-state"

const primary = "inline-flex h-9 items-center gap-1.5 rounded-lg bg-primary px-3.5 text-sm font-medium text-primary-foreground"
const secondary = "inline-flex h-9 items-center gap-1.5 rounded-lg border px-3.5 text-sm font-medium transition-colors hover:bg-accent"

export default function EmptyStateDemo() {
  return (
    <div className="grid w-full max-w-3xl gap-6 md:grid-cols-2">
      <EmptyState
        icon={<Inbox />}
        title="Inbox zero"
        description="New messages will land here. Enjoy the quiet while it lasts."
        actions={
          <button type="button" className={secondary}>
            Refresh
          </button>
        }
      />
      <EmptyState
        bordered
        icon={<Upload />}
        title="Drop files to upload"
        description="PNG, JPG or PDF up to 10 MB."
        actions={
          <>
            <button type="button" className={primary}>
              <Plus className="size-4" /> Browse files
            </button>
            <button type="button" className={secondary}>
              Import from URL
            </button>
          </>
        }
      />
      <EmptyState
        className="md:col-span-2"
        icon={<FileSearch />}
        title="No results for “quarterly”"
        description="Check the spelling, or try a broader term."
      />
    </div>
  )
}
```

## Source

```tsx title="components/empty-state.tsx"
import * as React from "react"

import { cn } from "@/lib/utils"

export interface EmptyStateProps extends Omit<React.ComponentProps<"div">, "title"> {
  icon?: React.ReactNode
  title: React.ReactNode
  description?: React.ReactNode
  /** Buttons or links rendered under the copy */
  actions?: React.ReactNode
  /** Dashed outline, good for drop areas and empty tables */
  bordered?: boolean
}

export function EmptyState({ icon, title, description, actions, bordered = false, className, ...props }: EmptyStateProps) {
  return (
    <div
      {...props}
      className={cn(
        "flex w-full flex-col items-center justify-center gap-4 rounded-2xl px-6 py-12 text-center",
        bordered && "border-2 border-dashed",
        className,
      )}
    >
      {icon && (
        <div className="relative">
          <span aria-hidden className="absolute inset-0 -z-10 scale-150 rounded-full bg-primary/10 blur-xl" />
          <div className="flex size-14 items-center justify-center rounded-2xl border bg-card text-muted-foreground shadow-sm [&_svg]:size-6">
            {icon}
          </div>
        </div>
      )}
      <div className="max-w-sm">
        <h3 className="text-base font-semibold">{title}</h3>
        {description && <p className="mt-1.5 text-sm leading-relaxed text-muted-foreground">{description}</p>}
      </div>
      {actions && <div className="flex flex-wrap items-center justify-center gap-2">{actions}</div>}
    </div>
  )
}
```
