Meet Frontfriend.Your brand. Your rules. Your agents, connected.Explore what’s new

Empty

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.