Empty

A component for displaying empty states with icons, titles, descriptions, and call-to-action buttons. Perfect for showing when no data is available or guiding users to take action.

Usage

Basic Empty State

A simple empty state with an icon, title, and description:

import { Empty, EmptyHeader, EmptyMedia, EmptyTitle, EmptyDescription } from "@prisma/eclipse";

export function BasicEmpty() {
  return (
    <Empty>
      <EmptyHeader>
        <EmptyMedia variant="icon">
          <i className="fa-duotone fa-regular fa-inbox"></i>
        </EmptyMedia>
        <EmptyTitle>No Data</EmptyTitle>
        <EmptyDescription>No data found</EmptyDescription>
      </EmptyHeader>
    </Empty>
  );
}

Live Example:

Live Example:

No Data
No data found

With Call-to-Action

Add buttons or actions to guide users:

import {
  Empty,
  EmptyHeader,
  EmptyMedia,
  EmptyTitle,
  EmptyDescription,
  EmptyContent,
  Button,
} from "@prisma/eclipse";

export function EmptyWithAction() {
  return (
    <Empty>
      <EmptyHeader>
        <EmptyMedia variant="icon">
          <i className="fa-duotone fa-regular fa-folder-open"></i>
        </EmptyMedia>
        <EmptyTitle>No Projects Yet</EmptyTitle>
        <EmptyDescription>
          You haven't created any projects yet. Get started by creating your first project.
        </EmptyDescription>
      </EmptyHeader>
      <EmptyContent>
        <Button>Create Project</Button>
      </EmptyContent>
    </Empty>
  );
}

Live Example:

Live Example:

No Projects Yet

You haven't created any projects yet. Get started by creating your first project.

With Multiple Actions

Include multiple buttons or links:

import {
  Empty,
  EmptyHeader,
  EmptyMedia,
  EmptyTitle,
  EmptyDescription,
  EmptyContent,
  Button,
} from "@prisma/eclipse";

export function EmptyMultipleActions() {
  return (
    <Empty>
      <EmptyHeader>
        <EmptyMedia variant="icon">
          <i className="fa-duotone fa-regular fa-folder-open"></i>
        </EmptyMedia>
        <EmptyTitle>No Projects Yet</EmptyTitle>
        <EmptyDescription>
          You haven't created any projects yet. Get started by creating your first project.
        </EmptyDescription>
      </EmptyHeader>
      <EmptyContent>
        <div className="flex gap-2">
          <Button>Create Project</Button>
          <Button variant="default-weak">Import Project</Button>
        </div>
        <a href="#" className="text-sm underline underline-offset-4">
          Learn More
        </a>
      </EmptyContent>
    </Empty>
  );
}

Live Example:

Live Example:

No Projects Yet

You haven't created any projects yet. Get started by creating your first project.

Learn More

Outline Variant

Add a border to create an outlined empty state:

import {
  Empty,
  EmptyHeader,
  EmptyMedia,
  EmptyTitle,
  EmptyDescription,
  EmptyContent,
  Button,
} from "@prisma/eclipse";

export function OutlineEmpty() {
  return (
    <Empty className="border">
      <EmptyHeader>
        <EmptyMedia variant="icon">
          <i className="fa-duotone fa-regular fa-cloud"></i>
        </EmptyMedia>
        <EmptyTitle>Cloud Storage Empty</EmptyTitle>
        <EmptyDescription>
          Upload files to your cloud storage to access them anywhere.
        </EmptyDescription>
      </EmptyHeader>
      <EmptyContent>
        <Button>Upload Files</Button>
      </EmptyContent>
    </Empty>
  );
}

Live Example:

Live Example:

Cloud Storage Empty

Upload files to your cloud storage to access them anywhere.

With Background

Add background colors or gradients:

import {
  Empty,
  EmptyHeader,
  EmptyMedia,
  EmptyTitle,
  EmptyDescription,
  EmptyContent,
  Button,
} from "@prisma/eclipse";

export function BackgroundEmpty() {
  return (
    <Empty className="bg-background-neutral">
      <EmptyHeader>
        <EmptyMedia variant="icon">
          <i className="fa-duotone fa-regular fa-bell"></i>
        </EmptyMedia>
        <EmptyTitle>No Notifications</EmptyTitle>
        <EmptyDescription>
          You're all caught up. New notifications will appear here.
        </EmptyDescription>
      </EmptyHeader>
      <EmptyContent>
        <Button variant="default-weak">Refresh</Button>
      </EmptyContent>
    </Empty>
  );
}

Live Example:

Live Example:

No Notifications
You're all caught up. New notifications will appear here.

Without Icon

Omit the icon for a simpler layout:

import {
  Empty,
  EmptyHeader,
  EmptyTitle,
  EmptyDescription,
  EmptyContent,
  Button,
} from "@prisma/eclipse";

export function SimpleEmpty() {
  return (
    <Empty>
      <EmptyHeader>
        <EmptyTitle>No Results Found</EmptyTitle>
        <EmptyDescription>
          Try adjusting your search or filter to find what you're looking for.
        </EmptyDescription>
      </EmptyHeader>
      <EmptyContent>
        <Button variant="default-weak">Clear Filters</Button>
      </EmptyContent>
    </Empty>
  );
}

Live Example:

Live Example:

No Results Found

Try adjusting your search or filter to find what you're looking for.

Search Empty State

With an input field for search:

import {
  Empty,
  EmptyHeader,
  EmptyMedia,
  EmptyTitle,
  EmptyDescription,
  EmptyContent,
  Input,
} from "@prisma/eclipse";

export function SearchEmpty() {
  return (
    <Empty className="border">
      <EmptyHeader>
        <EmptyMedia variant="icon">
          <i className="fa-duotone fa-regular fa-magnifying-glass"></i>
        </EmptyMedia>
        <EmptyTitle>404 - Not Found</EmptyTitle>
        <EmptyDescription>
          The page you're looking for doesn't exist. Try searching for what you need below.
        </EmptyDescription>
      </EmptyHeader>
      <EmptyContent>
        <Input placeholder="Search..." className="max-w-xs" />
        <a href="#" className="text-sm text-foreground-neutral-weak">
          Need help? Contact support
        </a>
      </EmptyContent>
    </Empty>
  );
}

Live Example:

Live Example:

404 - Not Found

The page you're looking for doesn't exist. Try searching for what you need below.

Large Icon Variant

Use larger icons for more prominent empty states:

import {
  Empty,
  EmptyHeader,
  EmptyMedia,
  EmptyTitle,
  EmptyDescription,
  EmptyContent,
  Button,
} from "@prisma/eclipse";

export function LargeIconEmpty() {
  return (
    <Empty>
      <EmptyHeader>
        <EmptyMedia>
          <i className="fa-duotone fa-regular fa-users"></i>
        </EmptyMedia>
        <EmptyTitle>No Team Members</EmptyTitle>
        <EmptyDescription>Invite your team to collaborate on this project.</EmptyDescription>
      </EmptyHeader>
      <EmptyContent>
        <Button>Invite Members</Button>
      </EmptyContent>
    </Empty>
  );
}

Live Example:

No Team Members
Invite your team to collaborate on this project.

API Reference

Empty

The main container component for the empty state. Wraps the EmptyHeader and EmptyContent components.

Props:

  • className - Additional CSS classes (string, optional)
  • All standard HTML div attributes
<Empty>
  <EmptyHeader />
  <EmptyContent />
</Empty>

EmptyHeader

Container for the empty state header, including the media, title, and description.

Props:

  • className - Additional CSS classes (string, optional)
  • All standard HTML div attributes
<EmptyHeader>
  <EmptyMedia />
  <EmptyTitle />
  <EmptyDescription />
</EmptyHeader>

EmptyMedia

Displays the media element of the empty state, such as an icon, image, or avatar.

Props:

  • variant - Visual variant ("default" | "icon", default: "default")
    • "default" - No background, transparent
    • "icon" - Muted background with rounded corners, sized for icons
  • className - Additional CSS classes (string, optional)
  • All standard HTML div attributes
// Icon variant with background
<EmptyMedia variant="icon">
  <Icon />
</EmptyMedia>

// Default variant for larger elements
<EmptyMedia>
  <Icon className="size-16" />
</EmptyMedia>

EmptyTitle

Displays the title of the empty state.

Props:

  • className - Additional CSS classes (string, optional)
  • All standard HTML div attributes
<EmptyTitle>No data</EmptyTitle>

EmptyDescription

Displays the description text of the empty state. Supports links with automatic styling.

Props:

  • className - Additional CSS classes (string, optional)
  • All standard HTML p attributes
<EmptyDescription>
  You do not have any notifications.
  <a href="#">Learn more</a>
</EmptyDescription>

EmptyContent

Container for action elements like buttons, inputs, or links in the empty state.

Props:

  • className - Additional CSS classes (string, optional)
  • All standard HTML div attributes
<EmptyContent>
  <Button>Add Project</Button>
</EmptyContent>

Features

  • ✅ Flexible composition with semantic components
  • ✅ Two media variants (default and icon)
  • ✅ Responsive design with proper spacing
  • ✅ Automatic link styling in descriptions
  • ✅ Centered text alignment
  • ✅ Balanced text layout for readability
  • ✅ Dashed border container
  • ✅ Supports custom backgrounds and borders
  • ✅ Icon sizing optimization
  • ✅ Fully typed with TypeScript
  • ✅ Customizable with className props

Best Practices

  • Use clear, action-oriented titles that describe the empty state
  • Provide helpful descriptions that guide users on what to do next
  • Always include at least one call-to-action button when possible
  • Use appropriate icons that represent the context (e.g., folder for files, bell for notifications)
  • Keep descriptions concise but informative
  • Use the icon variant for small icons, default for larger visuals
  • Consider adding a border or background for better visual separation
  • Ensure empty states are accessible with proper semantic HTML
  • Test empty states in different contexts (search, filters, first-time use)
  • Provide multiple action options when relevant (create, import, learn more)

Accessibility

The Empty component follows accessibility best practices:

  • Uses semantic HTML structure with proper div hierarchy
  • Supports keyboard navigation for interactive elements
  • Link styling with proper contrast ratios
  • Underlined links for clarity
  • Readable text sizes (text-sm)
  • Proper spacing for touch targets
  • Works with screen readers
  • Maintains proper focus order
  • All interactive elements are keyboard accessible

Common Use Cases

The Empty component is perfect for:

  • No data states - When lists or tables have no items
  • Search results - When searches return no matches
  • Filtered views - When filters exclude all items
  • Notifications - When there are no notifications
  • First-time use - Onboarding new users to features
  • File uploads - Empty file storage or folders
  • Team invites - No team members yet
  • 404 pages - Page not found errors
  • Completed tasks - All items processed or cleared
  • Settings - Features not yet configured
  • Archives - Empty archived content

Styling

The Empty component uses design tokens and can be customized:

  • Container: Dashed border, rounded corners, padding, centered content
  • Header: Vertical layout with gap spacing
  • Media (icon variant): Muted background, size-8, rounded-lg
  • Media (default variant): Transparent background for custom sizing
  • Title: Small font, medium weight, tracking-tight
  • Description: Small relaxed line height, muted foreground color
  • Links: Underlined with hover effects to primary color
  • Content: Flexible column layout with gap spacing

Customize by passing className props:

<Empty className="border-2 border-primary bg-primary/5">
  <EmptyHeader className="gap-4">
    <EmptyMedia variant="icon" className="bg-primary/10">
      <Icon className="text-primary" />
    </EmptyMedia>
    <EmptyTitle className="text-lg">Custom Styled</EmptyTitle>
  </EmptyHeader>
</Empty>

Design Patterns

Progressive Disclosure:

<Empty>
  <EmptyHeader>
    <EmptyMedia variant="icon">
      <Icon />
    </EmptyMedia>
    <EmptyTitle>Get Started</EmptyTitle>
    <EmptyDescription>Create your first item to begin.</EmptyDescription>
  </EmptyHeader>
  <EmptyContent>
    <Button>Quick Start</Button>
    <details className="text-sm text-foreground-neutral-weak">
      <summary className="cursor-pointer">Advanced options</summary>
      <div className="mt-2">Additional setup information...</div>
    </details>
  </EmptyContent>
</Empty>

Error Recovery:

<Empty className="border border-stroke-error">
  <EmptyHeader>
    <EmptyMedia variant="icon" className="bg-background-error text-foreground-error">
      <AlertCircle />
    </EmptyMedia>
    <EmptyTitle>Connection Error</EmptyTitle>
    <EmptyDescription>Unable to load data. Check your connection and try again.</EmptyDescription>
  </EmptyHeader>
  <EmptyContent>
    <Button onClick={retry}>Retry</Button>
  </EmptyContent>
</Empty>

Loading to Empty Transition:

{
  isLoading ? (
    <Spinner />
  ) : items.length === 0 ? (
    <Empty>
      <EmptyHeader>
        <EmptyMedia variant="icon">
          <Icon />
        </EmptyMedia>
        <EmptyTitle>No Items</EmptyTitle>
        <EmptyDescription>Add your first item to get started.</EmptyDescription>
      </EmptyHeader>
      <EmptyContent>
        <Button>Add Item</Button>
      </EmptyContent>
    </Empty>
  ) : (
    <ItemsList items={items} />
  );
}

On this page