Empty

Empty-state layout for lists, first-run screens, and errors.

Give empty lists, first-run screens, and errors a clear next step with media, a title, supporting text, and actions.

No Projects Yet
You haven't created any projects yet. Get started by creating your first project.
tsx
import { Button } from "@/components/ui/button"import {  Empty,  EmptyContent,  EmptyDescription,  EmptyHeader,  EmptyMedia,  EmptyTitle,} from "@/components/ui/empty"function FolderIcon(props: React.SVGProps<SVGSVGElement>) {  return (    <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true" {...props}>      <path d="M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z" />      <path d="M12 10v6" />      <path d="M9 13h6" />    </svg>  )}function ArrowUpRightIcon(props: React.SVGProps<SVGSVGElement>) {  return (    <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true" {...props}>      <path d="M7 7h10v10" />      <path d="M7 17 17 7" />    </svg>  )}export function Example() {  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>Create Project</Button>          <Button variant="secondary">Import Project</Button>        </div>        <Button variant="link" className="text-muted-foreground">          Learn More <ArrowUpRightIcon className="size-3.5" />        </Button>      </EmptyContent>    </Empty>  )}

Installation

npx @arctis-sh/@arctis-sh/ui@latest add empty

Usage

import {  Empty,  EmptyContent,  EmptyDescription,  EmptyHeader,  EmptyMedia,  EmptyTitle,} from "@/components/ui/empty"
<Empty>  <EmptyHeader>    <EmptyMedia variant="icon">      <Icon />    </EmptyMedia>    <EmptyTitle>No projects yet</EmptyTitle>    <EmptyDescription>Create one to get started.</EmptyDescription>  </EmptyHeader>  <EmptyContent>    <Button>New project</Button>  </EmptyContent></Empty>

Composition

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

Outline

Add a border for a more defined empty state.

Cloud Storage Empty
Upload files to your cloud storage to access them anywhere.
tsx
import { Button } from "@/components/ui/button"import {  Empty,  EmptyContent,  EmptyDescription,  EmptyHeader,  EmptyMedia,  EmptyTitle,} from "@/components/ui/empty"function CloudIcon(props: React.SVGProps<SVGSVGElement>) {  return (    <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true" {...props}>      <path d="M17.5 19H9a7 7 0 1 1 6.71-9h1.79a4.5 4.5 0 1 1 0 9Z" />    </svg>  )}export function Example() {  return (    <Empty className="border border-dashed border-border">      <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="secondary">Upload Files</Button>      </EmptyContent>    </Empty>  )}

Background

Add a background color or gradient directly to Empty.

No Notifications
You're all caught up. New notifications will appear here.
tsx
import { Button } from "@/components/ui/button"import {  Empty,  EmptyContent,  EmptyDescription,  EmptyHeader,  EmptyMedia,  EmptyTitle,} from "@/components/ui/empty"function BellIcon(props: React.SVGProps<SVGSVGElement>) {  return (    <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true" {...props}>      <path d="M6 8a6 6 0 0 1 12 0c0 7 3 9 3 9H3s3-2 3-9" />      <path d="M10.3 21a1.94 1.94 0 0 0 3.4 0" />    </svg>  )}function RefreshIcon(props: React.SVGProps<SVGSVGElement>) {  return (    <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true" {...props}>      <path d="M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8" />      <path d="M21 3v5h-5" />      <path d="M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16" />      <path d="M8 16H3v5" />    </svg>  )}export function Example() {  return (    <Empty className="rounded-none bg-muted">      <EmptyHeader>        <EmptyMedia variant="icon">          <BellIcon />        </EmptyMedia>        <EmptyTitle>No Notifications</EmptyTitle>        <EmptyDescription>          You&apos;re all caught up. New notifications will appear here.        </EmptyDescription>      </EmptyHeader>      <EmptyContent>        <Button variant="secondary">          <RefreshIcon className="size-3.5" />          Refresh        </Button>      </EmptyContent>    </Empty>  )}

Avatar

Place an avatar inside EmptyMedia.

Lucia ReyesLR
User Offline
This user is currently offline. You can leave a message to notify them or try again later.
tsx
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 Example() {  return (    <Empty>      <EmptyHeader>        <EmptyMedia>          <Avatar size="lg">            <AvatarImage src="/assets/brand/demos/avatars/avatar-6.png" alt="Lucia Reyes" />            <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 variant="secondary">Leave Message</Button>      </EmptyContent>    </Empty>  )}

Avatar Group

Place AvatarGroup inside EmptyMedia.

Mina ChoMCLREF
No Team Members
Invite your team to collaborate on this project.
tsx
import {  Avatar,  AvatarFallback,  AvatarGroup,  AvatarImage,} from "@/components/ui/avatar"import { Button } from "@/components/ui/button"import {  Empty,  EmptyContent,  EmptyDescription,  EmptyHeader,  EmptyMedia,  EmptyTitle,} from "@/components/ui/empty"function PlusIcon(props: React.SVGProps<SVGSVGElement>) {  return (    <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true" {...props}>      <path d="M5 12h14" />      <path d="M12 5v14" />    </svg>  )}export function Example() {  return (    <Empty>      <EmptyHeader>        <EmptyMedia>          <AvatarGroup>            <Avatar>              <AvatarImage src="/assets/brand/demos/avatars/avatar-1.png" alt="Mina Cho" />              <AvatarFallback>MC</AvatarFallback>            </Avatar>            <Avatar>              <AvatarFallback>LR</AvatarFallback>            </Avatar>            <Avatar>              <AvatarFallback>EF</AvatarFallback>            </Avatar>          </AvatarGroup>        </EmptyMedia>        <EmptyTitle>No Team Members</EmptyTitle>        <EmptyDescription>          Invite your team to collaborate on this project.        </EmptyDescription>      </EmptyHeader>      <EmptyContent>        <Button>          <PlusIcon className="size-3.5" />          Invite Members        </Button>      </EmptyContent>    </Empty>  )}

Input Group

Place InputGroup inside EmptyContent when the next step needs input.

404 - Not Found
The page you're looking for doesn't exist. Try searching for what you need below.
/
Need help? Contact support
tsx
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"function SearchIcon(props: React.SVGProps<SVGSVGElement>) {  return (    <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true" {...props}>      <circle cx="11" cy="11" r="8" />      <path d="m21 21-4.3-4.3" />    </svg>  )}export function Example() {  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 variant="filled" className="w-full max-w-xs">          <InputGroupInput placeholder="Search..." />          <InputGroupAddon>            <SearchIcon />          </InputGroupAddon>          <InputGroupAddon align="inline-end">            <Kbd>/</Kbd>          </InputGroupAddon>        </InputGroup>        <EmptyDescription>          Need help? <a href="#">Contact support</a>        </EmptyDescription>      </EmptyContent>    </Empty>  )}

API Reference

Empty

PropTypeDefault
classNamestring
<Empty>  <EmptyHeader />  <EmptyContent /></Empty>

EmptyHeader

PropTypeDefault
classNamestring
<EmptyHeader>  <EmptyMedia />  <EmptyTitle />  <EmptyDescription /></EmptyHeader>

EmptyMedia

PropTypeDefault
variant"default" | "icon""default"
classNamestring
<EmptyMedia variant="icon">  <Icon /></EmptyMedia>
<EmptyMedia>  <Avatar>    <AvatarFallback>AR</AvatarFallback>  </Avatar></EmptyMedia>

EmptyTitle

PropTypeDefault
classNamestring
<EmptyTitle>No projects yet</EmptyTitle>

EmptyDescription

PropTypeDefault
classNamestring
<EmptyDescription>Nothing to show here yet.</EmptyDescription>

EmptyContent

PropTypeDefault
classNamestring
<EmptyContent>  <Button>New project</Button></EmptyContent>