Empty-state layout primitives
No Projects Yet
You haven't created any projects yet. Get started by creating your first project.
import { Button } from '@/components/ui/button'
import { Empty, EmptyAction, EmptyDescription, EmptyHeader, EmptyMedia, EmptyTitle } from '@/components/ui/empty'
...import { Button } from '@/components/ui/button'
import { Empty, EmptyAction, EmptyDescription, EmptyHeader, EmptyMedia, EmptyTitle } from '@/components/ui/empty'
function FolderIcon() { return <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M3 6h7l2 2h9v11H3z" /></svg> }
export default function EmptyDemoExample() { return <Empty><EmptyHeader><EmptyMedia><FolderIcon /></EmptyMedia><EmptyTitle>No Projects Yet</EmptyTitle><EmptyDescription>You haven't created any projects yet. Get started by creating your first project.</EmptyDescription></EmptyHeader><EmptyAction><Button>Create Project</Button><Button variant="outline">Import Project</Button></EmptyAction><a href="#learn">Learn More ↗</a></Empty> }
Installation
npx @fictjs/shadcn@latest add emptyCopy the Fict shadcn component into your project.
Source
components/ui/empty.tsx
src/components/ui/empty.tsx
import { cn } from '@/lib/cn'
type DivProps = JSX.IntrinsicElements['div']
type HeadingProps = JSX.IntrinsicElements['h3']
type ParagraphProps = JSX.IntrinsicElements['p']
export function Empty(props: DivProps) {
const { class: className, ...rest } = props
return <div data-slot='empty' class={cn('flex min-h-40 flex-col items-center justify-center rounded-lg border border-dashed p-8 text-center', className)} {...rest} />
}
export function EmptyHeader(props: DivProps) {
const { class: className, ...rest } = props
return <div data-slot='empty-header' class={cn('flex max-w-sm flex-col items-center gap-2', className)} {...rest} />
}
export function EmptyMedia(props: DivProps) {
const { class: className, ...rest } = props
return <div data-slot='empty-media' class={cn('mb-2 flex size-12 items-center justify-center rounded-full bg-muted [&_svg]:size-6', className)} {...rest} />
}
export function EmptyTitle(props: HeadingProps) {
const { class: className, ...rest } = props
return <h3 data-slot='empty-title' class={cn('text-lg font-semibold', className)} {...rest} />
}
export function EmptyDescription(props: ParagraphProps) {
const { class: className, ...rest } = props
return <p data-slot='empty-description' class={cn('max-w-prose text-sm text-muted-foreground', className)} {...rest} />
}
export function EmptyAction(props: DivProps) {
const { class: className, ...rest } = props
return <div data-slot='empty-action' class={cn('mt-4 flex items-center justify-center gap-2', className)} {...rest} />
}
export function EmptyContent(props: DivProps) {
const { class: className, ...rest } = props
return <div data-slot='empty-content' class={cn('mt-4 flex w-full max-w-sm flex-col items-center gap-2', className)} {...rest} />
}
Usage
Examples
Outline
Cloud Storage Empty
Upload files to your cloud storage to access them anywhere.
import { Button } from '@/components/ui/button'
import { Empty, EmptyAction, EmptyDescription, EmptyHeader, EmptyMedia, EmptyTitle } from '@/components/ui/empty'
...import { Button } from '@/components/ui/button'
import { Empty, EmptyAction, EmptyDescription, EmptyHeader, EmptyMedia, EmptyTitle } from '@/components/ui/empty'
function CloudIcon() { return <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M17.5 19H6a4 4 0 0 1-.5-8A6.5 6.5 0 0 1 18 9.5a4.8 4.8 0 0 1-.5 9.5Z" /></svg> }
export default function EmptyOutlineExample() { return <Empty><EmptyHeader><EmptyMedia><CloudIcon /></EmptyMedia><EmptyTitle>Cloud Storage Empty</EmptyTitle><EmptyDescription>Upload files to your cloud storage to access them anywhere.</EmptyDescription></EmptyHeader><EmptyAction><Button size="sm" variant="outline">Upload Files</Button></EmptyAction></Empty> }
Background
No Notifications
You're all caught up. New notifications will appear here.
import { Button } from '@/components/ui/button'
import { Empty, EmptyAction, EmptyDescription, EmptyHeader, EmptyMedia, EmptyTitle } from '@/components/ui/empty'
...import { Button } from '@/components/ui/button'
import { Empty, EmptyAction, EmptyDescription, EmptyHeader, EmptyMedia, EmptyTitle } from '@/components/ui/empty'
function BellIcon() { return <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M18 8a6 6 0 0 0-12 0c0 7-3 7-3 9h18c0-2-3-2-3-9M10 21h4" /></svg> }
export default function EmptyBackgroundExample() { return <Empty class="bg-muted/50"><EmptyHeader><EmptyMedia><BellIcon /></EmptyMedia><EmptyTitle>No Notifications</EmptyTitle><EmptyDescription>You're all caught up. New notifications will appear here.</EmptyDescription></EmptyHeader><EmptyAction><Button variant="outline">Refresh</Button></EmptyAction></Empty> }
Avatar
User Offline
This user is currently offline. You can leave a message to notify them or try again later.
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'
import { Button } from '@/components/ui/button'
...import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'
import { Button } from '@/components/ui/button'
import { Empty, EmptyAction, EmptyDescription, EmptyHeader, EmptyTitle } from '@/components/ui/empty'
export default function EmptyAvatarExample() { return <Empty><EmptyHeader><Avatar size="lg"><AvatarImage src="/avatars/shadcn.jpg" alt="shadcn" /><AvatarFallback>CN</AvatarFallback></Avatar><EmptyTitle>User Offline</EmptyTitle><EmptyDescription>This user is currently offline. You can leave a message to notify them or try again later.</EmptyDescription></EmptyHeader><EmptyAction><Button size="sm">Leave Message</Button></EmptyAction></Empty> }
Avatar Group
No Team Members
Invite your team to collaborate on this project.
import { Avatar, AvatarFallback, AvatarGroup, AvatarImage } from '@/components/ui/avatar'
import { Button } from '@/components/ui/button'
...import { Avatar, AvatarFallback, AvatarGroup, AvatarImage } from '@/components/ui/avatar'
import { Button } from '@/components/ui/button'
import { Empty, EmptyAction, EmptyDescription, EmptyHeader, EmptyTitle } from '@/components/ui/empty'
export default function EmptyAvatarGroupExample() { return <Empty><EmptyHeader><AvatarGroup><Avatar size="lg"><AvatarImage src="/avatars/shadcn.jpg" alt="shadcn" /><AvatarFallback>CN</AvatarFallback></Avatar><Avatar size="lg"><AvatarImage src="/avatars/02.png" alt="maxleiter" /><AvatarFallback>ML</AvatarFallback></Avatar><Avatar size="lg"><AvatarImage src="/avatars/03.png" alt="evilrabbit" /><AvatarFallback>ER</AvatarFallback></Avatar></AvatarGroup><EmptyTitle>No Team Members</EmptyTitle><EmptyDescription>Invite your team to collaborate on this project.</EmptyDescription></EmptyHeader><EmptyAction><Button size="sm">Invite Members</Button></EmptyAction></Empty> }
Input Group
404 - Not Found
The page you're looking for doesn't exist. Try searching for what you need below.
Need help? Contact support
import { Empty, EmptyContent, EmptyDescription, EmptyHeader, EmptyTitle } from '@/components/ui/empty'
import { InputGroup, InputGroupAddon, InputGroupInput } from '@/components/ui/input-group'
...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 default function EmptyInputGroupExample() { 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><InputGroupInput type="search" aria-label="Search pages" placeholder="Try searching for pages..." /><InputGroupAddon><Kbd>/</Kbd></InputGroupAddon></InputGroup><p>Need help? <a href="#support">Contact support</a></p></EmptyContent></Empty> }
Rtl
لا توجد مشاريع بعد
لم تقم بإنشاء أي مشاريع بعد. ابدأ بإنشاء مشروعك الأول.
import { Button } from '@/components/ui/button'
import { Empty, EmptyAction, EmptyDescription, EmptyHeader, EmptyTitle } from '@/components/ui/empty'
...import { Button } from '@/components/ui/button'
import { Empty, EmptyAction, EmptyDescription, EmptyHeader, EmptyTitle } from '@/components/ui/empty'
const translations = { ar: ['لا توجد مشاريع بعد', 'لم تقم بإنشاء أي مشاريع بعد. ابدأ بإنشاء مشروعك الأول.', 'إنشاء مشروع', 'استيراد مشروع', 'تعرف على المزيد'], he: ['אין פרויקטים עדיין', 'עדיין לא יצרת פרויקטים. התחל על ידי יצירת הפרויקט הראשון שלך.', 'צור פרויקט', 'ייבא פרויקט', 'למד עוד'], en: ['No Projects Yet', "You haven't created any projects yet. Get started by creating your first project.", 'Create Project', 'Import Project', 'Learn More'] } as const
export default function EmptyRtlExample() { let language = $state<keyof typeof translations>('ar'); const direction = () => language === 'en' ? 'ltr' : 'rtl'; const text = () => translations[language]; return <div class="grid gap-4"><select value={language} onChange={event => { language = event.currentTarget.value as keyof typeof translations }}><option value="ar">Arabic (العربية)</option><option value="he">Hebrew (עברית)</option><option value="en">English</option></select><Empty dir={direction()}><EmptyHeader><EmptyTitle>{text()[0]}</EmptyTitle><EmptyDescription>{text()[1]}</EmptyDescription></EmptyHeader><EmptyAction><Button>{text()[2]}</Button><Button variant="outline">{text()[3]}</Button></EmptyAction><a href="#learn">{text()[4]} ↗</a></Empty></div> }