EmptyState

General page-level empty/zero state with icon, title, body and actions.

import { EmptyState }from "@/components/ui"

Zero state

No messages yet

When someone sends you a message, it'll show up here.

Full source

The complete component, read from src/components/ui/empty-state.tsx.

empty-state.tsx
"use client";

import { type ReactNode } from "react";
import type { LucideIcon } from "lucide-react";
import { cn } from "@/lib/utils";

interface EmptyStateProps {
  icon?: LucideIcon;
  title: string;
  body?: string;

  actions?: ReactNode;

  compact?: boolean;
  className?: string;
}

export function EmptyState({ icon: Icon, title, body, actions, compact, className }: EmptyStateProps) {
  return (
    <div
      className={cn(
        "flex flex-col items-center rounded-xl border border-dashed border-border bg-surface text-center",
        compact ? "px-6 py-8" : "px-6 py-16",
        className
      )}
    >
      {Icon && (
        <span className="mb-4 flex size-12 items-center justify-center rounded-full bg-accent-light text-accent">
          <Icon size={22} aria-hidden />
        </span>
      )}
      <h3 className="text-base font-semibold text-text">{title}</h3>
      {body && <p className="mx-auto mt-1 max-w-sm text-sm leading-relaxed text-text-secondary">{body}</p>}
      {actions && <div className="mt-5 flex flex-wrap items-center justify-center gap-2">{actions}</div>}
    </div>
  );
}

Dependencies

To use EmptyState you also need the following. Copy these into your project alongside it.

Related components

Recommended folder structure

project structure
src/components/ui/
  ├─ button.tsx
  └─ empty-state.tsx
src/hooks/
  ├─ use-mounted.ts
  └─ use-popover-position.ts
src/lib/
  └─ utils.ts