Empty

A flexible, accessible composition for empty states.

No messages yet

When you receive a message, it will appear here.

vue
<script setup lang="ts">
import { Icon } from '@iconify/vue'
import {
  Button,
  Empty,
  EmptyContent,
  EmptyDescription,
  EmptyHeader,
  EmptyMedia,
  EmptyTitle,
} from '@typlog/ui'
</script>

<template>
  <Empty class="min-h-64 border border-dashed border-gray-6">
    <EmptyHeader>
      <EmptyMedia variant="icon">
        <Icon icon="lucide:inbox" />
      </EmptyMedia>
      <EmptyTitle>No messages yet</EmptyTitle>
      <EmptyDescription>
        When you receive a message, it will appear here.
      </EmptyDescription>
    </EmptyHeader>
    <EmptyContent>
      <Button>Compose a message</Button>
      <Button variant="outline">Refresh</Button>
    </EmptyContent>
  </Empty>
</template>

API Reference ​

Empty ​

PropDefaultType
radius
–
"none""small""medium""large""full"

Overrides the radius inherited from the theme.

size"2"
"1""2""3"

Controls the spacing and typography of the empty state.

EmptyHeader ​

EmptyMedia ​

PropDefaultType
variant"default"
"default""icon"

Controls whether the media is presented as a plain visual or an icon tile.

EmptyTitle ​

EmptyDescription ​

EmptyContent ​

Examples ​

Size ​

Nothing here

New items will appear here.

Nothing here

New items will appear here.

Nothing here

New items will appear here.

vue
<script setup lang="ts">
import { Icon } from '@iconify/vue'
import {
  Empty,
  EmptyDescription,
  EmptyHeader,
  EmptyMedia,
  EmptyTitle,
} from '@typlog/ui'

const sizes = ['1', '2', '3'] as const
</script>

<template>
  <div class="flex flex-col w-full gap-4">
    <Empty
      v-for="size in sizes"
      :key="size"
      :size="size"
      class="min-w-0 border border-gray-6 bg-gray-2"
    >
      <EmptyHeader>
        <EmptyMedia variant="icon">
          <Icon icon="lucide:inbox" />
        </EmptyMedia>
        <EmptyTitle>Nothing here</EmptyTitle>
        <EmptyDescription>
          New items will appear here.
        </EmptyDescription>
      </EmptyHeader>
    </Empty>
  </div>
</template>

Radius ​

none

small

medium

large

full

vue
<script setup lang="ts">
import {
  Empty,
  EmptyTitle,
} from '@typlog/ui'

const radii = ['none', 'small', 'medium', 'large', 'full'] as const
</script>

<template>
  <div class="flex flex-col w-full gap-4">
    <Empty
      v-for="radius in radii"
      :key="radius"
      size="1"
      :radius="radius"
      class="min-w-0 min-h-20 border border-gray-7 bg-gray-2"
    >
      <EmptyTitle>{{ radius }}</EmptyTitle>
    </Empty>
  </div>
</template>

Media variant ​

EmptyMedia can be rendered as a plain media area or an icon tile.

Empty folder

Upload a file to get started.

No notifications

You're all caught up.

vue
<script setup lang="ts">
import { Icon } from '@iconify/vue'
import {
  Button,
  Empty,
  EmptyContent,
  EmptyDescription,
  EmptyHeader,
  EmptyMedia,
  EmptyTitle,
} from '@typlog/ui'
</script>

<template>
  <div class="flex flex-col w-full gap-4">
    <Empty class="min-w-0 border border-gray-6 bg-gray-2">
      <EmptyHeader>
        <EmptyMedia>
          <Icon icon="lucide:folder-open" />
        </EmptyMedia>
        <EmptyTitle>Empty folder</EmptyTitle>
        <EmptyDescription>Upload a file to get started.</EmptyDescription>
      </EmptyHeader>
      <EmptyContent>
        <Button size="1">Upload file</Button>
      </EmptyContent>
    </Empty>
    <Empty class="min-w-0 border border-gray-6 bg-gray-2">
      <EmptyHeader>
        <EmptyMedia variant="icon">
          <Icon icon="lucide:inbox" />
        </EmptyMedia>
        <EmptyTitle>No notifications</EmptyTitle>
        <EmptyDescription>You're all caught up.</EmptyDescription>
      </EmptyHeader>
      <EmptyContent>
        <Button size="1" variant="outline">Refresh</Button>
      </EmptyContent>
    </Empty>
  </div>
</template>
Last updated Sep 15, 2026