Skip to content

Empty ​

A placeholder for empty states — no data, no search results, a blank list, or a first-run screen. It centers a piece of media (usually an icon), a title, a description, and optionally an action, inside a dashed container.

Empty is a compound component: compose the parts you need. Only the root is required; everything inside is optional.

Anatomy ​

Empty                    ← the dashed container; centers its content
├── EmptyHeader          ← groups the media, title, and description
│   ├── EmptyMedia       ← the icon or illustration (variant: icon | default)
│   ├── EmptyTitle       ← the headline
│   └── EmptyDescription ← supporting text
└── EmptyContent         ← optional area for actions (buttons, links)

Usage ​

BasicAn icon, title, and description.
<script setup lang="ts">
import {
    Empty,
    EmptyHeader,
    EmptyMedia,
    EmptyTitle,
    EmptyDescription,
} from "@dlbcodes/ui";
import { PhFolderOpen } from "@phosphor-icons/vue";
</script>

<template>
    <Empty>
        <EmptyHeader>
            <EmptyMedia variant="icon">
                <PhFolderOpen class="size-5" />
            </EmptyMedia>
            <EmptyTitle>No projects yet</EmptyTitle>
            <EmptyDescription>
                You haven't created any projects. Get started by creating your
                first one.
            </EmptyDescription>
        </EmptyHeader>
    </Empty>
</template>

With an action ​

Use EmptyContent to add a call to action below the header.

With actionAn empty state with a button.
<script setup lang="ts">
import {
    Empty,
    EmptyHeader,
    EmptyMedia,
    EmptyTitle,
    EmptyDescription,
    EmptyContent,
    Button,
} from "@dlbcodes/ui";
import { PhPlus, PhRocket } from "@phosphor-icons/vue";
</script>

<template>
    <Empty>
        <EmptyHeader>
            <EmptyMedia variant="icon">
                <PhRocket class="size-5" />
            </EmptyMedia>
            <EmptyTitle>Launch your first campaign</EmptyTitle>
            <EmptyDescription>
                Campaigns help you reach your audience. Create one to begin.
            </EmptyDescription>
        </EmptyHeader>
        <EmptyContent>
            <Button variant="primary">
                <PhPlus class="size-4" />
                New campaign
            </Button>
        </EmptyContent>
    </Empty>
</template>

No results ​

A common use is the "no search results" state.

No resultsFor empty search results.
<script setup lang="ts">
import {
    Empty,
    EmptyHeader,
    EmptyMedia,
    EmptyTitle,
    EmptyDescription,
} from "@dlbcodes/ui";
import { PhMagnifyingGlass } from "@phosphor-icons/vue";
</script>

<template>
    <Empty>
        <EmptyHeader>
            <EmptyMedia variant="icon">
                <PhMagnifyingGlass class="size-5" />
            </EmptyMedia>
            <EmptyTitle>No results found</EmptyTitle>
            <EmptyDescription>
                We couldn't find anything matching your search. Try a different
                term.
            </EmptyDescription>
        </EmptyHeader>
    </Empty>
</template>

Media variants ​

EmptyMedia has two variants. icon wraps the media in a subtle rounded background; default (the default) renders the media as-is — useful for a larger icon or a custom illustration.

Default mediaA larger icon with no background.
<script setup lang="ts">
import {
    Empty,
    EmptyHeader,
    EmptyMedia,
    EmptyTitle,
    EmptyDescription,
} from "@dlbcodes/ui";
import { PhTray } from "@phosphor-icons/vue";
</script>

<template>
    <Empty>
        <EmptyHeader>
            <EmptyMedia>
                <PhTray class="size-10 text-text-tertiary" />
            </EmptyMedia>
            <EmptyTitle>Your inbox is empty</EmptyTitle>
            <EmptyDescription>
                New messages will appear here.
            </EmptyDescription>
        </EmptyHeader>
    </Empty>
</template>

Appearance ​

The container's border uses the theme's --border, the icon background uses --muted, and the text uses --foreground and --muted-foreground. Corners follow --radius. So an empty state follows your theme, including dark mode. See Theming.

Props ​

EmptyMedia ​

PropTypeDefaultDescription
variant"default" | "icon""default"icon adds a rounded subtle background; default is bare.
classstring—Classes merged onto the media wrapper.

Empty / EmptyHeader / EmptyTitle / EmptyDescription / EmptyContent ​

Each takes only class. Their slot content is what's rendered:

  • Empty — the dashed container; centers everything.
  • EmptyHeader — groups media, title, and description.
  • EmptyTitle — the headline text.
  • EmptyDescription — supporting text. Links inside it are underlined and take the brand color on hover.
  • EmptyContent — an area for actions, below the header.

Composition ​

Only Empty is required. Use as many or as few of the parts as you need — a title alone, or the full media + title + description + action stack.