codefast/ui

Command Palette

Search for a command to run...

Source
Display

Empty

Empty-state layout with media, title, description, and action slots.

Examples

Avatar

Use the EmptyMedia component to display an avatar in the empty state.

LR
User Offline

This user is currently offline. You can leave a message to notify them or try again later.

25 lines
import { Avatar, AvatarFallback, AvatarImage } from "@codefast/ui/avatar";
import { Button } from "@codefast/ui/button";
import { Empty, EmptyContent, EmptyDescription, EmptyHeader, EmptyMedia, EmptyTitle } from "@codefast/ui/empty";

export function EmptyAvatar() {
  return (
    <Empty>
      <EmptyHeader>
        <EmptyMedia variant="default">
          <Avatar className="size-12">
            <AvatarImage src="https://github.com/codefastlabs.png" className="grayscale" />
            <AvatarFallback>LR</AvatarFallback>
          </Avatar>
        </EmptyMedia>
        <EmptyTitle>User Offline</EmptyTitle>
        <EmptyDescription>
          This user is currently offline. You can leave a message to notify them or try again later.
        </EmptyDescription>
      </EmptyHeader>
      <EmptyContent>
        <Button size="sm">Leave Message</Button>
      </EmptyContent>
    </Empty>
  );
}

Avatar Group

Use the EmptyMedia component to display an avatar group in the empty state.

CNLRER
No Team Members

Invite your team to collaborate on this project.

43 lines
import { Avatar, AvatarFallback, AvatarImage } from "@codefast/ui/avatar";
import { Button } from "@codefast/ui/button";
import { Empty, EmptyContent, EmptyDescription, EmptyHeader, EmptyMedia, EmptyTitle } from "@codefast/ui/empty";
import { PlusIcon } from "lucide-react";

export function EmptyAvatarGroup() {
  return (
    <Empty>
      <EmptyHeader>
        <EmptyMedia>
          <div className="flex -space-x-2 *:data-[slot=avatar]:size-12 *:data-[slot=avatar]:ring-2 *:data-[slot=avatar]:ring-background *:data-[slot=avatar]:grayscale">
            <Avatar>
              <AvatarImage src="https://github.com/codefastlabs.png" alt="@codefast" />
              <AvatarFallback>CN</AvatarFallback>
            </Avatar>
            <Avatar>
              <AvatarImage
                src="https://images.unsplash.com/photo-1500648767791-00dcc994a43e?w=128&h=128&fit=crop&q=80"
                alt="@leo"
              />
              <AvatarFallback>LR</AvatarFallback>
            </Avatar>
            <Avatar>
              <AvatarImage
                src="https://images.unsplash.com/photo-1438761681033-6461ffad8d80?w=128&h=128&fit=crop&q=80"
                alt="@ava"
              />
              <AvatarFallback>ER</AvatarFallback>
            </Avatar>
          </div>
        </EmptyMedia>
        <EmptyTitle>No Team Members</EmptyTitle>
        <EmptyDescription>Invite your team to collaborate on this project.</EmptyDescription>
      </EmptyHeader>
      <EmptyContent>
        <Button size="sm">
          <PlusIcon />
          Invite Members
        </Button>
      </EmptyContent>
    </Empty>
  );
}

Background

Use the bg-* and bg-gradient-* utilities to add a background to the empty state.

No Notifications

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

25 lines
import { Button } from "@codefast/ui/button";
import { Empty, EmptyContent, EmptyDescription, EmptyHeader, EmptyMedia, EmptyTitle } from "@codefast/ui/empty";
import { BellIcon, RefreshCcwIcon } from "lucide-react";

export function EmptyMuted() {
  return (
    <Empty className="h-full bg-muted/30">
      <EmptyHeader>
        <EmptyMedia variant="icon">
          <BellIcon />
        </EmptyMedia>
        <EmptyTitle>No Notifications</EmptyTitle>
        <EmptyDescription className="max-w-xs text-pretty">
          You&apos;re all caught up. New notifications will appear here.
        </EmptyDescription>
      </EmptyHeader>
      <EmptyContent>
        <Button variant="outline">
          <RefreshCcwIcon />
          Refresh
        </Button>
      </EmptyContent>
    </Empty>
  );
}

Card

Wrap the Empty component in a Card to create an empty-state card.

No projects yet

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

32 lines
import { Button } from "@codefast/ui/button";
import { Empty, EmptyContent, EmptyDescription, EmptyHeader, EmptyMedia, EmptyTitle } from "@codefast/ui/empty";
import { ArrowUpRightIcon, FolderIcon } from "lucide-react";

export function EmptyInCard() {
  return (
    <Empty>
      <EmptyHeader>
        <EmptyMedia variant="icon">
          <FolderIcon />
        </EmptyMedia>
        <EmptyTitle>No projects yet</EmptyTitle>
        <EmptyDescription>
          You haven&apos;t created any projects yet. Get started by creating your first project.
        </EmptyDescription>
      </EmptyHeader>
      <EmptyContent>
        <div className="flex gap-2">
          <Button asChild>
            <a href="/">Create project</a>
          </Button>
          <Button variant="outline">Import project</Button>
        </div>
        <Button variant="link" asChild className="text-muted-foreground">
          <a href="/">
            Learn more <ArrowUpRightIcon />
          </a>
        </Button>
      </EmptyContent>
    </Empty>
  );
}

InputGroup

You can add an InputGroup component to the EmptyContent component.

404 - Not Found

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

/

Need help? Contact support

31 lines
import { Empty, EmptyContent, EmptyDescription, EmptyHeader, EmptyTitle } from "@codefast/ui/empty";
import { InputGroup, InputGroupAddon, InputGroupInput } from "@codefast/ui/input-group";
import { Kbd } from "@codefast/ui/kbd";
import { SearchIcon } from "lucide-react";

export function EmptyInputGroup() {
  return (
    <Empty>
      <EmptyHeader>
        <EmptyTitle>404 - Not Found</EmptyTitle>
        <EmptyDescription>
          The page you&apos;re looking for doesn&apos;t exist. Try searching for what you need below.
        </EmptyDescription>
      </EmptyHeader>
      <EmptyContent>
        <InputGroup className="sm:w-3/4">
          <InputGroupInput placeholder="Try searching for pages..." />
          <InputGroupAddon>
            <SearchIcon />
          </InputGroupAddon>
          <InputGroupAddon align="inline-end">
            <Kbd>/</Kbd>
          </InputGroupAddon>
        </InputGroup>
        <EmptyDescription>
          Need help? <a href="/">Contact support</a>
        </EmptyDescription>
      </EmptyContent>
    </Empty>
  );
}

Outline

Use the border utility class to create an outline empty state.

Cloud Storage Empty

Upload files to your cloud storage to access them anywhere.

22 lines
import { Button } from "@codefast/ui/button";
import { Empty, EmptyContent, EmptyDescription, EmptyHeader, EmptyMedia, EmptyTitle } from "@codefast/ui/empty";
import { CloudIcon } from "lucide-react";

export function EmptyOutline() {
  return (
    <Empty className="border border-dashed">
      <EmptyHeader>
        <EmptyMedia variant="icon">
          <CloudIcon />
        </EmptyMedia>
        <EmptyTitle>Cloud Storage Empty</EmptyTitle>
        <EmptyDescription>Upload files to your cloud storage to access them anywhere.</EmptyDescription>
      </EmptyHeader>
      <EmptyContent>
        <Button variant="outline" size="sm">
          Upload Files
        </Button>
      </EmptyContent>
    </Empty>
  );
}

RTL

Right-to-left layout support for languages such as Arabic and Hebrew.

Translations are AI-generated for demonstration and may be imperfect.

لا توجد مشاريع بعد

لم تقم بإنشاء أي مشاريع بعد. ابدأ بإنشاء مشروعك الأول.

تعرف على المزيد
64 lines
import { Button } from "@codefast/ui/button";
import { Empty, EmptyContent, EmptyDescription, EmptyHeader, EmptyMedia, EmptyTitle } from "@codefast/ui/empty";
import { ArrowUpRightIcon, FolderCodeIcon } from "lucide-react";

import type { Translations } from "#/features/components-catalog/components/detail/language";
import { useTranslation } from "#/features/components-catalog/components/detail/language-context";

const translations: Translations = {
  en: {
    dir: "ltr",
    values: {
      title: "No Projects Yet",
      description: "You haven't created any projects yet. Get started by creating your first project.",
      createProject: "Create Project",
      importProject: "Import Project",
      learnMore: "Learn More",
    },
  },
  ar: {
    dir: "rtl",
    values: {
      title: "لا توجد مشاريع بعد",
      description: "لم تقم بإنشاء أي مشاريع بعد. ابدأ بإنشاء مشروعك الأول.",
      createProject: "إنشاء مشروع",
      importProject: "استيراد مشروع",
      learnMore: "تعرف على المزيد",
    },
  },
  he: {
    dir: "rtl",
    values: {
      title: "אין פרויקטים עדיין",
      description: "עדיין לא יצרת פרויקטים. התחל על ידי יצירת הפרויקט הראשון שלך.",
      createProject: "צור פרויקט",
      importProject: "ייבא פרויקט",
      learnMore: "למד עוד",
    },
  },
};

export function EmptyRtl() {
  const { dir, t } = useTranslation(translations, "ar");

  return (
    <Empty dir={dir}>
      <EmptyHeader>
        <EmptyMedia variant="icon">
          <FolderCodeIcon />
        </EmptyMedia>
        <EmptyTitle>{t.title}</EmptyTitle>
        <EmptyDescription>{t.description}</EmptyDescription>
      </EmptyHeader>
      <EmptyContent className="flex-row justify-center gap-2">
        <Button>{t.createProject}</Button>
        <Button variant="outline">{t.importProject}</Button>
      </EmptyContent>
      <Button variant="link" asChild className="text-muted-foreground" size="sm">
        <a href="/">
          {t.learnMore} <ArrowUpRightIcon className="rtl:rotate-270" data-icon="inline-end" />
        </a>
      </Button>
    </Empty>
  );
}

Anatomy

How the parts nest — every slot the component exposes, in composition order.

Empty
├── EmptyHeader
│ ├── EmptyMedia
│ ├── EmptyTitle
│ └── EmptyDescription
└── EmptyContent

Features

  • border-dashed is already baked into Empty's default styling — add a border (width) utility yourself to actually render the dashed outline.
  • Composes with Card (wrap Empty in a Card) or an InputGroup inside EmptyContent for empty-search patterns.

API reference

Props for each part of the component. All native element props are also forwarded.

Empty

Layout wrapper for an empty/zero state.

childrenReactNode

Compose EmptyHeader (media, title, description) and EmptyContent (action).

EmptyMedia

Icon or illustration slot above the title.

variant"default" | "icon"

Render as plain media, or "icon" to frame it in a rounded, muted background.

Default"default"

childrenReactNode

The icon or illustration itself.

EmptyTitle

Headline for the empty state.

childrenReactNode

The headline text.

EmptyDescription

Supporting text under the title.

childrenReactNode

The supporting copy.

EmptyContent

Action slot below the header.

childrenReactNode

Buttons, links, or other calls to action.

Accessibility

Built to be keyboard-navigable and screen-reader friendly out of the box.

  • Use a real heading for EmptyTitle so it fits the document outline.
  • Always give users a next step — a button or link out of the empty state.
  • Keep the icon decorative; the meaning lives in the title and description.

Guidelines

Conventions that keep usage consistent across an app.

Do

  • Explain why it’s empty and what to do next.
  • Offer a single, primary action.

Don’t

  • Don’t leave a blank area with no guidance.
  • Don’t pile on multiple competing actions.

Explore further

Ready to integrate?

Follow the Getting Started guide to install @codefast/ui, or browse the full component gallery.