Use the Empty component to display an empty state.
Interactive preview
Rendered from the shared Frontfriend stories with React Base, the same
preview Studio shows. Styling comes from your Design System; the code below
is native to Reka UI.
Installation
Complete project setup first. Your configuration
must select vue/reka and include your Design System ID.
pnpm exec frontfriend download empty -f vue --primitive reka
Review dependencies reported by the downloader and keep your source
changes in version control.
Usage
<script setup lang="ts">import { Empty } from "@/components/ui/empty";import { EmptyHeader } from "@/components/ui/empty";import { EmptyMedia } from "@/components/ui/empty";import { Icon } from "@/components/ui/icon";import { EmptyTitle } from "@/components/ui/empty";import { EmptyDescription } from "@/components/ui/empty";import * as UI from "@/components/ui/empty";const Component = UI.Empty;const args = { "title": "No projects yet", "description": "Create your first project.", "variant": "icon",} satisfies Partial< InstanceType<typeof Component> extends { $props: infer Props } ? Props : {}> & Record<string, unknown> & { variant?: "default" | "icon" };</script><template> <Empty ><EmptyHeader ><EmptyMedia :variant="args.variant"><Icon name="Folder" /></EmptyMedia ><EmptyTitle>{{ args.title }}</EmptyTitle ><EmptyDescription>{{ args.description }}</EmptyDescription></EmptyHeader ></Empty ></template>
Examples
Default
<script setup lang="ts">import { Empty } from "@/components/ui/empty";import { EmptyHeader } from "@/components/ui/empty";import { EmptyMedia } from "@/components/ui/empty";import { FolderCodeIcon } from "lucide-vue-next";import { EmptyTitle } from "@/components/ui/empty";import { EmptyDescription } from "@/components/ui/empty";import { EmptyContent } from "@/components/ui/empty";import { Button } from "@/components/ui/button";import { ArrowUpRightIcon } from "lucide-vue-next";import * as UI from "@/components/ui/empty";const Component = UI.Empty;const args = {} satisfies Partial< InstanceType<typeof Component> extends { $props: infer Props } ? Props : {}> & Record<string, unknown>;</script><template> <Empty v-bind="args" ><EmptyHeader ><EmptyMedia variant="icon"><FolderCodeIcon /></EmptyMedia ><EmptyTitle>No Projects Yet</EmptyTitle ><EmptyDescription >You haven't created any projects yet. Get started by creating your first project.</EmptyDescription ></EmptyHeader ><EmptyContent class="flex-row justify-center gap-2" ><Button>Create Project</Button ><Button variant="outline">Import Project</Button></EmptyContent ><Button variant="link" as-child class="text-neutral-mid" size="sm" ><a href="#">Learn More <ArrowUpRightIcon /></a></Button ></Empty></template>
Outline
<script setup lang="ts">import { Empty } from "@/components/ui/empty";import { EmptyHeader } from "@/components/ui/empty";import { EmptyMedia } from "@/components/ui/empty";import { CloudIcon } from "lucide-vue-next";import { EmptyTitle } from "@/components/ui/empty";import { EmptyDescription } from "@/components/ui/empty";import { EmptyContent } from "@/components/ui/empty";import { Button } from "@/components/ui/button";import * as UI from "@/components/ui/empty";const Component = UI.Empty;const args = {} satisfies Partial< InstanceType<typeof Component> extends { $props: infer Props } ? Props : {}> & Record<string, unknown>;</script><template> <Empty class="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 ></template>
Background
<script setup lang="ts">import { Empty } from "@/components/ui/empty";import { EmptyHeader } from "@/components/ui/empty";import { EmptyMedia } from "@/components/ui/empty";import { BellIcon } from "lucide-vue-next";import { EmptyTitle } from "@/components/ui/empty";import { EmptyDescription } from "@/components/ui/empty";import { EmptyContent } from "@/components/ui/empty";import { Button } from "@/components/ui/button";import { RefreshCcwIcon } from "lucide-vue-next";import * as UI from "@/components/ui/empty";const Component = UI.Empty;const args = {} satisfies Partial< InstanceType<typeof Component> extends { $props: infer Props } ? Props : {}> & Record<string, unknown>;</script><template> <Empty class="bg-neutral-subtle/30" ><EmptyHeader ><EmptyMedia variant="icon"><BellIcon /></EmptyMedia ><EmptyTitle>No Notifications</EmptyTitle ><EmptyDescription class="max-w-xs text-pretty" >You're all caught up. New notifications will appear here.</EmptyDescription ></EmptyHeader ><EmptyContent ><Button variant="outline" ><RefreshCcwIcon />Refresh</Button ></EmptyContent ></Empty ></template>
Avatar
<script setup lang="ts">import { Empty } from "@/components/ui/empty";import { EmptyHeader } from "@/components/ui/empty";import { EmptyMedia } from "@/components/ui/empty";import { Avatar } from "@/components/ui/avatar";import { AvatarImage } from "@/components/ui/avatar";import { AvatarFallback } from "@/components/ui/avatar";import { EmptyTitle } from "@/components/ui/empty";import { EmptyDescription } from "@/components/ui/empty";import { EmptyContent } from "@/components/ui/empty";import { Button } from "@/components/ui/button";import * as UI from "@/components/ui/empty";const Component = UI.Empty;const args = {} satisfies Partial< InstanceType<typeof Component> extends { $props: infer Props } ? Props : {}> & Record<string, unknown>;</script><template> <Empty ><EmptyHeader ><EmptyMedia variant="default" ><Avatar class="size-12" ><AvatarImage src="https://github.com/shadcn.png" class="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 ></template>
With Input Group
<script setup lang="ts">import { Empty } from "@/components/ui/empty";import { EmptyHeader } from "@/components/ui/empty";import { EmptyTitle } from "@/components/ui/empty";import { EmptyDescription } from "@/components/ui/empty";import { EmptyContent } from "@/components/ui/empty";import { InputGroup } from "@/components/ui/input-group";import { InputGroupInput } from "@/components/ui/input-group";import { InputGroupAddon } from "@/components/ui/input-group";import { SearchIcon } from "lucide-vue-next";import { Kbd } from "@/components/ui/kbd";import * as UI from "@/components/ui/empty";const Component = UI.Empty;const args = {} satisfies Partial< InstanceType<typeof Component> extends { $props: infer Props } ? Props : {}> & Record<string, unknown>;</script><template> <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 class="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 ></template>
API reference
Frontfriend-owned props. Native HTML and inherited primitive props
remain documented by Reka UI.
Empty
Prop
Type
Default
Description
class
HTMLAttributes["class"]
—
EmptyContent
Prop
Type
Default
Description
class
HTMLAttributes["class"]
—
EmptyDescription
Prop
Type
Default
Description
class
HTMLAttributes["class"]
—
EmptyHeader
Prop
Type
Default
Description
class
HTMLAttributes["class"]
—
EmptyMedia
Prop
Type
Default
Description
class
HTMLAttributes["class"]
—
variant
EmptyMediaVariants["variant"]
—
EmptyTitle
Prop
Type
Default
Description
class
HTMLAttributes["class"]
—
Configuration determines the actual classes and available styling hooks.
See Theming & component configuration before introducing
a repeated local override.