Skip to content
ssntl/ui

Empty state

An empty screen that says what will appear and offers the action that fills it.

No projects yet

Projects you create appear here, with their status and next delivery.

Installation

$ pnpm dlx shadcn@latest add @ssntl/empty-state

Usage

tsx
import { EmptyState, EmptyStateActions, EmptyStateDescription, EmptyStateIcon, EmptyStateTitle } from "@/components/ui/empty-state"

<EmptyState>
  <EmptyStateIcon><FolderPlus /></EmptyStateIcon>
  <EmptyStateTitle>No projects yet</EmptyStateTitle>
  <EmptyStateDescription>Projects you create appear here.</EmptyStateDescription>
  <EmptyStateActions><Button>Create project</Button></EmptyStateActions>
</EmptyState>
  • Say what will appear here and why it is empty; never just “No data”.
  • Offer the one action that fills it, as the primary button.
  • The icon is decorative (aria-hidden); the title carries the meaning.

Code

components/ui/empty-state.tsx
import * as React from "react"

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

// An empty screen invites the next action: say what will appear here, why it
// is empty, and offer the one action that fills it. Never just "No data".
function EmptyState({ className, ...props }: React.ComponentProps<"div">) {
  return (
    <div
      data-slot="empty-state"
      className={cn(
        "flex min-w-0 flex-col items-center gap-2 rounded-lg bg-muted px-6 py-12 text-center",
        className
      )}
      {...props}
    />
  )
}

function EmptyStateIcon({ className, ...props }: React.ComponentProps<"div">) {
  return (
    <div
      aria-hidden
      data-slot="empty-state-icon"
      className={cn(
        "mb-2 grid size-11 place-items-center rounded-full bg-secondary text-foreground [&_svg]:size-5",
        className
      )}
      {...props}
    />
  )
}

function EmptyStateTitle({ className, ...props }: React.ComponentProps<"h3">) {
  return (
    <h3
      data-slot="empty-state-title"
      className={cn("text-heading", className)}
      {...props}
    />
  )
}

function EmptyStateDescription({
  className,
  ...props
}: React.ComponentProps<"p">) {
  return (
    <p
      data-slot="empty-state-description"
      className={cn(
        "max-w-sm text-body text-balance text-muted-foreground",
        className
      )}
      {...props}
    />
  )
}

function EmptyStateActions({
  className,
  ...props
}: React.ComponentProps<"div">) {
  return (
    <div
      data-slot="empty-state-actions"
      className={cn("mt-4 flex flex-wrap justify-center gap-2", className)}
      {...props}
    />
  )
}

export {
  EmptyState,
  EmptyStateActions,
  EmptyStateDescription,
  EmptyStateIcon,
  EmptyStateTitle,
}