import { ArrowUpRightIcon, FolderCode } from "lucide-react";import { Button, buttonVariants } from "@/components/ui/button";import { Empty, EmptyContent, EmptyDescription, EmptyHeader, EmptyMedia, EmptyTitle,} from "@/components/ui/empty";
export function EmptyDemo() { return ( <Empty> <EmptyHeader> <EmptyMedia variant="icon"> <FolderCode /> </EmptyMedia> <EmptyTitle>No Projects Yet</EmptyTitle> <EmptyDescription> You haven't created any projects yet. Get started by creating your first project. </EmptyDescription> </EmptyHeader> <EmptyContent className="flex-row justify-center gap-2"> <Button>Create Project</Button> <Button variant="outline">Import Project</Button> </EmptyContent> <a className={buttonVariants({ variant: "link", size: "sm" })} href="#"> Learn More <ArrowUpRightIcon data-icon="inline-end" /> </a> </Empty> );}pnpm dlx shadcn@latest add @herocn/emptyimport { Empty, EmptyContent, EmptyDescription, EmptyHeader, EmptyMedia, EmptyTitle,} from "@/components/ui/empty"<Empty> <EmptyHeader> <EmptyMedia variant="icon"> <Icon /> </EmptyMedia> <EmptyTitle>No data</EmptyTitle> <EmptyDescription>No data found</EmptyDescription> </EmptyHeader> <EmptyContent> <Button>Add data</Button> </EmptyContent></Empty>Use the following composition to build an Empty state:
Empty├── EmptyHeader│ ├── EmptyMedia│ ├── EmptyTitle│ └── EmptyDescription└── EmptyContentUse a dashed border to make an outlined empty state.
import { Cloud } from "lucide-react";
import { Button } from "@/components/ui/button";import { Empty, EmptyContent, EmptyDescription, EmptyHeader, EmptyMedia, EmptyTitle,} from "@/components/ui/empty";
export function EmptyOutline() { return ( <Empty className="border border-dashed"> <EmptyHeader> <EmptyMedia variant="icon"> <Cloud /> </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> );}Add a muted background for low-contrast states.
import { Bell, RefreshCcwIcon } from "lucide-react";
import { Button } from "@/components/ui/button";import { Empty, EmptyContent, EmptyDescription, EmptyHeader, EmptyMedia, EmptyTitle,} from "@/components/ui/empty";
export function EmptyBackground() { return ( <Empty className="h-full bg-muted/30"> <EmptyHeader> <EmptyMedia variant="icon"> <Bell /> </EmptyMedia> <EmptyTitle>No Notifications</EmptyTitle> <EmptyDescription className="max-w-xs text-pretty"> You're all caught up. New notifications will appear here. </EmptyDescription> </EmptyHeader> <EmptyContent> <Button variant="outline"> <RefreshCcwIcon data-icon="inline-start" /> Refresh </Button> </EmptyContent> </Empty> );}Use EmptyMedia to render an avatar-based empty state.
import { Avatar, AvatarFallback, AvatarImage,} from "@/components/ui/avatar";import { Button } from "@/components/ui/button";import { Empty, EmptyContent, EmptyDescription, EmptyHeader, EmptyMedia, EmptyTitle,} from "@/components/ui/empty";
export function EmptyAvatar() { return ( <Empty> <EmptyHeader> <EmptyMedia variant="default"> <Avatar className="size-12"> <AvatarImage src="https://github.com/Maqed.png" /> <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> );}Show a group of avatars to indicate team or collaborator context.
import { PlusIcon } from "lucide-react";
import { Avatar, AvatarFallback, AvatarImage,} from "@/components/ui/avatar";import { Button } from "@/components/ui/button";import { Empty, EmptyContent, EmptyDescription, EmptyHeader, EmptyMedia, EmptyTitle,} from "@/components/ui/empty";
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"> <Avatar> <AvatarImage src="https://github.com/Maqed.png" alt="@0xMaqed" /> <AvatarFallback>Mqd</AvatarFallback> </Avatar> <Avatar> <AvatarImage src="https://github.com/shadcn.png" alt="@shadcn" /> <AvatarFallback>CN</AvatarFallback> </Avatar> <Avatar> <AvatarImage src="https://github.com/evilrabbit.png" alt="@evilrabbit" /> <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 data-icon="inline-start" /> Invite Members </Button> </EmptyContent> </Empty> );}Place interactive search or filter controls in EmptyContent.
import { SearchIcon } from "lucide-react";
import { Empty, EmptyContent, EmptyDescription, EmptyHeader, EmptyTitle,} from "@/components/ui/empty";import { InputGroup, InputGroupAddon, InputGroupInput,} from "@/components/ui/input-group";import { Kbd } from "@/components/ui/kbd";
export function EmptyInputGroup() { return ( <Empty> <EmptyHeader> <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> <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> );}"use client";
import { ArrowUpRightIcon, FolderCode } from "lucide-react";
import { type Translations, useTranslation,} from "@/components/language-selector";import { Button } from "@/components/ui/button";import { Empty, EmptyContent, EmptyDescription, EmptyHeader, EmptyMedia, EmptyTitle,} from "@/components/ui/empty";
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, language, t } = useTranslation(translations, "ar");
return ( <div lang={language} dir={dir}> <Empty> <EmptyHeader> <EmptyMedia variant="icon"> <FolderCode /> </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" render={<a href="#" />} className="text-muted-foreground" size="sm" nativeButton={false} > {t.learnMore} <ArrowUpRightIcon className="rtl:rotate-270" data-icon="inline-end" /> </Button> </Empty> </div> );}The root container for the empty state.
| Prop | Type | Default |
|---|---|---|
| className | string | — |
Wraps the media, title, and description.
| Prop | Type | Default |
|---|---|---|
| className | string | — |
Displays an icon, image, avatar, or any custom media.
| Prop | Type | Default |
|---|---|---|
| variant | "default" | "icon" | "default" |
| className | string | — |
The primary heading for the empty state.
| Prop | Type | Default |
|---|---|---|
| className | string | — |
Secondary text that explains the empty state.
| Prop | Type | Default |
|---|---|---|
| className | string | — |
Action area for buttons, inputs, or links.
| Prop | Type | Default |
|---|---|---|
| className | string | — |