

<Preview name="EmptyStateDefaultExample" />

## Overview [#overview]

The **EmptyState** component provides a consistent way to communicate that a section has no content to display. By default it fills the available parent space, centers its content, and renders a solid surface shell. Use `variant="ghost"` when the parent already owns the surrounding surface, and use `variant="overlay"` when the empty state should sit above existing content.

## Usage [#usage]

```tsx
import {
  EmptyState,
  EmptyStateDescription,
  EmptyStateHeading,
  EmptyStateMedia,
  EmptyStateTitle,
} from "@tilt-legal/cubitt-components/primitives";
import { Inbox } from "@tilt-legal/cubitt-icons/ui/outline";
```

```tsx
<EmptyState>
  <EmptyStateMedia>
    <Inbox />
  </EmptyStateMedia>
  <EmptyStateHeading>
    <EmptyStateTitle>No projects found</EmptyStateTitle>
    <EmptyStateDescription>
      Create your first project to get started with your workflow.
    </EmptyStateDescription>
  </EmptyStateHeading>
</EmptyState>
```

## Examples [#examples]

### Ghost [#ghost]

Use `variant="ghost"` when the parent already provides the border, background, or padding.

<Tabs items="['Preview', 'Code']">
  <Tab value="Preview">
    <Preview name="EmptyStateCustomIconExample" />
  </Tab>

  <Tab value="Code">
    ```tsx
    import {
      EmptyState,
      EmptyStateDescription,
      EmptyStateHeading,
      EmptyStateMedia,
      EmptyStateTitle,
    } from "@tilt-legal/cubitt-components/primitives";
    import { Folder } from "@tilt-legal/cubitt-icons/ui/outline";

    <EmptyState variant="ghost">
      <EmptyStateMedia>
        <Folder />
      </EmptyStateMedia>
      <EmptyStateHeading>
        <EmptyStateTitle>No folders</EmptyStateTitle>
        <EmptyStateDescription>
          Create a folder to organize your files.
        </EmptyStateDescription>
      </EmptyStateHeading>
    </EmptyState>
    ```
  </Tab>
</Tabs>

### Overlay [#overlay]

Use `variant="overlay"` when the empty state should sit on top of existing content, such as a table with skeleton rows.

<Tabs items="['Preview', 'Code']">
  <Tab value="Preview">
    <Preview name="EmptyStateOverlayExample" />
  </Tab>

  <Tab value="Code">
    ```tsx
    import {
      EmptyState,
      EmptyStateDescription,
      EmptyStateHeading,
      EmptyStateMedia,
      EmptyStateTitle,
      Skeleton,
      Table,
      TableBody,
      TableCell,
      TableHead,
      TableHeader,
      TableRow,
    } from "@tilt-legal/cubitt-components/primitives";
    import { Magnifier } from "@tilt-legal/cubitt-icons/ui/outline";

    <div className="relative">
      <EmptyState className="top-10" variant="overlay">
        <EmptyStateMedia>
          <Magnifier />
        </EmptyStateMedia>
        <EmptyStateHeading>
          <EmptyStateTitle>No results</EmptyStateTitle>
          <EmptyStateDescription>
            Try adjusting your filters.
          </EmptyStateDescription>
        </EmptyStateHeading>
      </EmptyState>
      <Table>
        <TableHeader>
          <TableRow>
            <TableHead className="w-48">Name</TableHead>
            <TableHead className="w-64">Description</TableHead>
            <TableHead className="w-32">Status</TableHead>
            <TableHead className="w-24 text-right">Actions</TableHead>
          </TableRow>
        </TableHeader>
        <TableBody>
          <TableRow className="opacity-30 [&_[data-slot=skeleton]]:animate-none">
            <TableCell>
              <Skeleton className="h-4 w-28" />
            </TableCell>
            <TableCell>
              <Skeleton className="h-4 w-40" />
            </TableCell>
            <TableCell>
              <Skeleton className="h-4 w-16" />
            </TableCell>
            <TableCell className="text-right">
              <Skeleton className="ml-auto h-4 w-16" />
            </TableCell>
          </TableRow>
          <TableRow className="opacity-30 [&_[data-slot=skeleton]]:animate-none">
            <TableCell>
              <Skeleton className="h-4 w-32" />
            </TableCell>
            <TableCell>
              <Skeleton className="h-4 w-36" />
            </TableCell>
            <TableCell>
              <Skeleton className="h-4 w-20" />
            </TableCell>
            <TableCell className="text-right">
              <Skeleton className="ml-auto h-4 w-12" />
            </TableCell>
          </TableRow>
          <TableRow className="opacity-30 [&_[data-slot=skeleton]]:animate-none">
            <TableCell>
              <Skeleton className="h-4 w-24" />
            </TableCell>
            <TableCell>
              <Skeleton className="h-4 w-44" />
            </TableCell>
            <TableCell>
              <Skeleton className="h-4 w-14" />
            </TableCell>
            <TableCell className="text-right">
              <Skeleton className="ml-auto h-4 w-20" />
            </TableCell>
          </TableRow>
          <TableRow className="opacity-30 [&_[data-slot=skeleton]]:animate-none">
            <TableCell>
              <Skeleton className="h-4 w-28" />
            </TableCell>
            <TableCell>
              <Skeleton className="h-4 w-32" />
            </TableCell>
            <TableCell>
              <Skeleton className="h-4 w-18" />
            </TableCell>
            <TableCell className="text-right">
              <Skeleton className="ml-auto h-4 w-14" />
            </TableCell>
          </TableRow>
          <TableRow className="opacity-30 [&_[data-slot=skeleton]]:animate-none">
            <TableCell>
              <Skeleton className="h-4 w-20" />
            </TableCell>
            <TableCell>
              <Skeleton className="h-4 w-40" />
            </TableCell>
            <TableCell>
              <Skeleton className="h-4 w-16" />
            </TableCell>
            <TableCell className="text-right">
              <Skeleton className="ml-auto h-4 w-16" />
            </TableCell>
          </TableRow>
          <TableRow className="opacity-30 [&_[data-slot=skeleton]]:animate-none">
            <TableCell>
              <Skeleton className="h-4 w-30" />
            </TableCell>
            <TableCell>
              <Skeleton className="h-4 w-38" />
            </TableCell>
            <TableCell>
              <Skeleton className="h-4 w-18" />
            </TableCell>
            <TableCell className="text-right">
              <Skeleton className="ml-auto h-4 w-10" />
            </TableCell>
          </TableRow>
          <TableRow className="opacity-30 [&_[data-slot=skeleton]]:animate-none">
            <TableCell>
              <Skeleton className="h-4 w-26" />
            </TableCell>
            <TableCell>
              <Skeleton className="h-4 w-34" />
            </TableCell>
            <TableCell>
              <Skeleton className="h-4 w-22" />
            </TableCell>
            <TableCell className="text-right">
              <Skeleton className="ml-auto h-4 w-18" />
            </TableCell>
          </TableRow>
        </TableBody>
      </Table>
    </div>
    ```
  </Tab>
</Tabs>

### With Action Button [#with-action-button]

Use `EmptyStateAction` to add a call-to-action inside the empty state.

<Tabs items="['Preview', 'Code']">
  <Tab value="Preview">
    <Preview name="EmptyStateWithActionExample" />
  </Tab>

  <Tab value="Code">
    ```tsx
    import {
      Button,
      EmptyState,
      EmptyStateAction,
      EmptyStateDescription,
      EmptyStateHeading,
      EmptyStateMedia,
      EmptyStateTitle,
    } from "@tilt-legal/cubitt-components/primitives";
    import { File, Plus } from "@tilt-legal/cubitt-icons/ui/outline";

    <EmptyState>
      <EmptyStateMedia>
        <File />
      </EmptyStateMedia>
      <EmptyStateHeading>
        <EmptyStateTitle>No documents</EmptyStateTitle>
        <EmptyStateDescription>
          Upload or create your first document to get started.
        </EmptyStateDescription>
      </EmptyStateHeading>
      <EmptyStateAction>
        <Button onClick={() => undefined}>
          <Plus />
          Create Document
        </Button>
      </EmptyStateAction>
    </EmptyState>
    ```
  </Tab>
</Tabs>

### Error State [#error-state]

Set `error` on `EmptyState` to apply error styling to `EmptyStateMedia` and `EmptyStateTitle`.

<Tabs items="['Preview', 'Code']">
  <Tab value="Preview">
    <Preview name="EmptyStateErrorExample" />
  </Tab>

  <Tab value="Code">
    ```tsx
    import {
      Button,
      EmptyState,
      EmptyStateAction,
      EmptyStateDescription,
      EmptyStateHeading,
      EmptyStateMedia,
      EmptyStateTitle,
    } from "@tilt-legal/cubitt-components/primitives";
    import {
      RefreshAnticlockwise,
      TriangleWarning,
    } from "@tilt-legal/cubitt-icons/ui/outline";

    <EmptyState error>
      <EmptyStateMedia>
        <TriangleWarning />
      </EmptyStateMedia>
      <EmptyStateHeading>
        <EmptyStateTitle>Something went wrong</EmptyStateTitle>
        <EmptyStateDescription>
          We couldn't load your data. Please try again.
        </EmptyStateDescription>
      </EmptyStateHeading>
      <EmptyStateAction>
        <Button onClick={() => undefined} variant="secondary">
          <RefreshAnticlockwise />
          Try Again
        </Button>
      </EmptyStateAction>
    </EmptyState>
    ```
  </Tab>
</Tabs>

## API Reference [#api-reference]

### EmptyState [#emptystate]

The root container component. By default it fills the available parent space with centered content and generous padding.

| Prop        | Type                                | Default     | Description                                                                                                                               |
| ----------- | ----------------------------------- | ----------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
| `variant`   | `"default" \| "ghost" \| "overlay"` | `"default"` | `default` renders the solid shell, `ghost` removes shell styling, and `overlay` positions the state absolutely with a gradient background |
| `error`     | `boolean`                           | `false`     | Enables error styling for `EmptyStateMedia` and `EmptyStateTitle`                                                                         |
| `className` | `string`                            | -           | Additional CSS classes. Use this to override the default fill, spacing, or surface styling                                                |
| `children`  | `React.ReactNode`                   | -           | Child components                                                                                                                          |

### EmptyStateMedia [#emptystatemedia]

Container for the icon with a circular background. Automatically applies error styling when the parent has `error`.

| Prop        | Type                        | Default     | Description                             |
| ----------- | --------------------------- | ----------- | --------------------------------------- |
| `size`      | `"sm" \| "default" \| "lg"` | `"default"` | Controls the media circle and icon size |
| `className` | `string`                    | -           | Additional CSS classes                  |
| `children`  | `React.ReactNode`           | -           | Icon element                            |

### EmptyStateHeading [#emptystateheading]

Container for the title and description.

| Prop        | Type              | Default | Description            |
| ----------- | ----------------- | ------- | ---------------------- |
| `className` | `string`          | -       | Additional CSS classes |
| `children`  | `React.ReactNode` | -       | Title and description  |

### EmptyStateTitle [#emptystatetitle]

The title text. Automatically applies error styling when the parent has `error`.

| Prop        | Type              | Default | Description            |
| ----------- | ----------------- | ------- | ---------------------- |
| `className` | `string`          | -       | Additional CSS classes |
| `children`  | `React.ReactNode` | -       | Title content          |

### EmptyStateDescription [#emptystatedescription]

The description text.

| Prop        | Type              | Default | Description            |
| ----------- | ----------------- | ------- | ---------------------- |
| `className` | `string`          | -       | Additional CSS classes |
| `children`  | `React.ReactNode` | -       | Description content    |

### EmptyStateAction [#emptystateaction]

Container for action buttons.

| Prop        | Type              | Default | Description            |
| ----------- | ----------------- | ------- | ---------------------- |
| `className` | `string`          | -       | Additional CSS classes |
| `children`  | `React.ReactNode` | -       | Button element(s)      |

## Design Guidelines [#design-guidelines]

### When to Use [#when-to-use]

* Empty lists, tables, or grids
* Search results with no matches
* Error states when data fails to load
* First-time user experiences

### Best Practices [#best-practices]

* Always provide a clear, actionable title
* Include a helpful description explaining what the user can do
* Use `variant="ghost"` when a parent container already owns the surrounding shell
* Use `variant="overlay"` when content should stay mounted underneath
* When appropriate, include an action button to help users resolve the empty state
* Use the error state for failed data loading, not for validation errors
