# Empty

## Overview

A structured zero-state container rendered when a list, table, or search returns no results. Uses a composable primitive pattern (`Empty + EmptyIcon + EmptyTitle + EmptyDescription + EmptyAction`) to provide a flexible, consistent empty-state experience.

---

## When to Use

- A table or list with zero records
- A search query that returns no results
- A page section with no data yet ("No projects created yet")

## When NOT to Use

- Loading states — use `<Skeleton>` while fetching.
- Error states — use `<Alert variant="destructive">` when a fetch fails.

---

## Anatomy

```
<Empty>
  <EmptyIcon>
    <SearchX className="size-10 text-muted-foreground" />
  </EmptyIcon>
  <EmptyTitle>No results found</EmptyTitle>
  <EmptyDescription>Try adjusting your search or filters.</EmptyDescription>
  <EmptyAction>
    <Button>Clear filters</Button>
  </EmptyAction>
</Empty>
```

---

## Sub-Components

| Component          | Description                                                                    |
| ------------------ | ------------------------------------------------------------------------------ |
| `Empty`            | Root container with dashed border, centered flex layout, and fade-in animation |
| `EmptyIcon`        | Circular muted-background icon container                                       |
| `EmptyImage`       | Optional image element with muted opacity                                      |
| `EmptyTitle`       | Main heading (`<h3>`) with semibold foreground text                            |
| `EmptyDescription` | Muted supplementary text (`<p>`)                                               |
| `EmptyAction`      | CTA button area — renders as flex row on desktop, stacked on mobile            |

All sub-components accept `className` and forward all HTML div/p/h3 attributes.

---

## Examples

### Standard No-Data Empty State

```tsx
import { Empty, EmptyIcon, EmptyTitle, EmptyDescription, EmptyAction, Button } from 'xertica-ui/ui';
import { Users } from 'lucide-react';

<Empty>
  <EmptyIcon>
    <Users className="size-10 text-muted-foreground" />
  </EmptyIcon>
  <EmptyTitle>No members yet</EmptyTitle>
  <EmptyDescription>Get started by adding your first team member.</EmptyDescription>
  <EmptyAction>
    <Button onClick={() => setDialogOpen(true)}>Add Member</Button>
  </EmptyAction>
</Empty>;
```

### Empty Search Results

```tsx
import { SearchX } from 'lucide-react';

<Empty>
  <EmptyIcon>
    <SearchX className="size-10 text-muted-foreground" />
  </EmptyIcon>
  <EmptyTitle>No results found</EmptyTitle>
  <EmptyDescription>
    No records matching "{searchQuery}". Try a different search term.
  </EmptyDescription>
</Empty>;
```

### In Table Body

```tsx
<TableBody>
  {records.length === 0 ? (
    <TableRow>
      <TableCell colSpan={5} className="h-48 p-0">
        <Empty>
          <EmptyIcon>
            <FileX className="size-8 text-muted-foreground" />
          </EmptyIcon>
          <EmptyTitle>No records found</EmptyTitle>
        </Empty>
      </TableCell>
    </TableRow>
  ) : (
    records.map(r => <RecordRow key={r.id} record={r} />)
  )}
</TableBody>
```

---

## AI Rules

- **Always use the composable sub-component pattern** — `Empty > EmptyIcon > EmptyTitle`. Do not pass flat props like `title=""` or `icon={}`.
- Icons inside `<EmptyIcon>` always use `className="size-10 text-muted-foreground"` (or `size-8` for compact table contexts).
- `<EmptyAction>` is optional — only include it when there is a clear next action for the user.
- When used inside a table, wrap it in `<TableCell colSpan={totalColumns}>` to span the full table width.
- `Empty` renders with `min-h-[400px]` — in compact table rows, override with `className="min-h-[200px]"`.

---

## Related Components

- [`Skeleton`](./skeleton.md) — Loading state before data arrives
- [`Alert`](./alert.md) — Error state when fetching fails
- [`Table`](./table.md) — Primary context for empty states
