Empty
Usage
Use the Empty component to display a placeholder state when there is no content to show.
No projects found
Title Soon
Use the title prop to set the title of the empty state.
No projects found
<template>
<PEmpty title="No projects found" />
</template>h2 by default. Use the as prop with an object to change it, for example :as="{ title: 'h3' }". You can also set the root element this way: :as="{ root: 'section', title: 'h3' }".Description
Use the description prop to set the description of the empty state.
No projects found
<template>
<PEmpty
title="No projects found"
description="It looks like you haven't added any projects. Create one to get started."
/>
</template>Icon
Use the icon prop to set the icon of the empty state.
No projects found
<template>
<PEmpty
icon="i-lucide-file"
title="No projects found"
description="It looks like you haven't added any projects. Create one to get started."
/>
</template>Avatar
Use the avatar prop to set the avatar of the empty state.
No projects found
<template>
<PEmpty
:avatar="{
src: 'https://github.com/nuxt.png'
}"
title="No projects found"
description="It looks like you haven't added any projects. Create one to get started."
/>
</template>Loading
Use the loading prop to show a loading icon in place of the icon. The layout stays identical, so you can toggle between loading and empty states without layout shifts.
Loading projects
<template>
<PEmpty
icon="i-lucide-file"
loading
title="Loading projects"
description="Please wait while we fetch your projects."
/>
</template>Loading Icon
Use the loading-icon prop to customize the loading icon. Defaults to i-lucide-loader-circle.
Loading projects
<template>
<PEmpty
icon="i-lucide-file"
loading
loading-icon="i-lucide-loader"
title="Loading projects"
description="Please wait while we fetch your projects."
/>
</template>Actions
Use the actions prop to add some Button actions to the empty state.
No projects found
<template>
<PEmpty
icon="i-lucide-file"
title="No projects found"
description="It looks like you haven't added any projects. Create one to get started."
:actions="[
{
icon: 'i-lucide-plus',
label: 'Create new'
},
{
icon: 'i-lucide-refresh-cw',
label: 'Refresh',
color: 'neutral',
variant: 'subtle'
}
]"
/>
</template>Variant
Use the variant prop to change the variant of the empty state.
No notifications
<template>
<PEmpty
variant="naked"
icon="i-lucide-bell"
title="No notifications"
description="You're all caught up. New notifications will appear here."
:actions="[
{
icon: 'i-lucide-refresh-cw',
label: 'Refresh',
color: 'neutral',
variant: 'subtle'
}
]"
/>
</template>Size
Use the size prop to change the size of the empty state.
No notifications
<template>
<PEmpty
size="xl"
icon="i-lucide-bell"
title="No notifications"
description="You're all caught up. New notifications will appear here."
:actions="[
{
icon: 'i-lucide-refresh-cw',
label: 'Refresh',
color: 'neutral',
variant: 'subtle'
}
]"
/>
</template>Examples
With slots
Use the available slots to create a more complex empty state.
<script setup lang="ts">
import type { UserProps } from 'pohon-ui';
const members: Array<UserProps> = [
{
name: 'Daniel Roe',
description: 'danielroe',
to: 'https://github.com/danielroe',
target: '_blank',
avatar: {
src: 'https://github.com/danielroe.png',
alt: 'danielroe',
loading: 'lazy' as const,
},
},
{
name: 'Pooya Parsa',
description: 'pi0',
to: 'https://github.com/pi0',
target: '_blank',
avatar: {
src: 'https://github.com/pi0.png',
alt: 'pi0',
loading: 'lazy' as const,
},
},
{
name: 'Sébastien Chopin',
description: 'atinux',
to: 'https://github.com/atinux',
target: '_blank',
avatar: {
src: 'https://github.com/atinux.png',
alt: 'atinux',
loading: 'lazy' as const,
},
},
{
name: 'Benjamin Canac',
description: 'benjamincanac',
to: 'https://github.com/benjamincanac',
target: '_blank',
avatar: {
src: 'https://github.com/benjamincanac.png',
alt: 'benjamincanac',
loading: 'lazy' as const,
},
},
];
</script>
<template>
<PEmpty
title="No team members"
description="Invite your team to collaborate on this project."
variant="naked"
:actions="[{
label: 'Invite members',
icon: 'i-lucide-user-plus',
color: 'neutral',
}]"
>
<template #leading>
<PAvatarGroup size="xl">
<PAvatar
src="https://github.com/nuxt.png"
alt="Nuxt"
loading="lazy"
/>
<PAvatar
src="https://github.com/unjs.png"
alt="Unjs"
loading="lazy"
/>
</PAvatarGroup>
</template>
<template #footer>
<PSeparator class="my-4" />
<div class="gap-4 grid grid-cols-2">
<PPageCard
v-for="(member, index) in members"
:key="index"
:to="member.to"
:ui="{ container: 'sm:p-4' }"
>
<PUser
:avatar="member.avatar"
:name="member.name"
:description="member.description"
:ui="{ name: 'truncate' }"
/>
</PPageCard>
</div>
</template>
</PEmpty>
</template>
API
Props
| Prop | Default | Type |
|---|---|---|
as | 'div' | anyThe element or component this component should render as. |
icon | anyThe icon displayed above the title. | |
avatar | AvatarProps | |
loading | boolean When | |
loadingIcon | appConfig.ui.icons.loading | anyThe icon when the |
title | string | |
description | string | |
actions | ButtonProps[]Display a list of Button in the body. | |
variant | 'outline' | "outline" | "solid" | "soft" | "subtle" | "naked" |
size | 'md' | "md" | "xs" | "sm" | "lg" | "xl" |
ui | { root?: SlotClass; header?: SlotClass; avatar?: SlotClass; title?: SlotClass; description?: SlotClass; body?: SlotClass; actions?: SlotClass; footer?: SlotClass; } |
Slots
| Slot | Type |
|---|---|
header | {} |
leading | { ui: object; } |
title | {} |
description | {} |
body | {} |
actions | {} |
footer | {} |
Theme
export default defineAppConfig({
ui: {
empty: {
slots: {
root: 'relative flex flex-col items-center justify-center gap-4 rounded-lg p-4 sm:p-6 lg:p-8 min-w-0',
header: 'flex flex-col items-center gap-2 max-w-sm text-center',
avatar: 'shrink-0 mb-2',
title: 'color-text-highlighted text-pretty font-500',
description: 'text-balance text-center',
body: 'flex flex-col items-center gap-4 max-w-sm',
actions: 'flex flex-wrap justify-center gap-2 shrink-0',
footer: 'flex flex-col items-center gap-2 max-w-sm'
},
variants: {
size: {
xs: {
avatar: 'pohon:size-8 text-base',
title: 'text-sm',
description: 'text-xs'
},
sm: {
avatar: 'pohon:size-9 text-lg',
title: 'text-sm',
description: 'text-xs'
},
md: {
avatar: 'pohon:size-10 text-xl',
title: 'text-base',
description: 'text-sm'
},
lg: {
avatar: 'pohon:size-11 text-[22px]',
title: 'text-base',
description: 'text-sm'
},
xl: {
avatar: 'pohon:size-12 text-2xl',
title: 'text-lg',
description: 'text-base'
}
},
variant: {
solid: {
root: 'bg-background-inverted',
title: 'color-text-inverted',
description: 'color-text-dimmed'
},
outline: {
root: 'bg-background ring ring-border',
description: 'color-text-muted'
},
soft: {
root: 'bg-background-elevated/50',
description: 'color-text-toned'
},
subtle: {
root: 'bg-background-elevated/50 ring ring-border',
description: 'color-text-toned'
},
naked: {
description: 'color-text-muted'
}
},
loading: {
true: {
avatar: '[&>[data-slot=icon]]:animate-spin'
}
}
},
defaultVariants: {
variant: 'outline',
size: 'md'
}
}
}
})import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import ui from 'pohon-ui/vite'
export default defineConfig({
plugins: [
vue(),
ui({
ui: {
empty: {
slots: {
root: 'relative flex flex-col items-center justify-center gap-4 rounded-lg p-4 sm:p-6 lg:p-8 min-w-0',
header: 'flex flex-col items-center gap-2 max-w-sm text-center',
avatar: 'shrink-0 mb-2',
title: 'color-text-highlighted text-pretty font-500',
description: 'text-balance text-center',
body: 'flex flex-col items-center gap-4 max-w-sm',
actions: 'flex flex-wrap justify-center gap-2 shrink-0',
footer: 'flex flex-col items-center gap-2 max-w-sm'
},
variants: {
size: {
xs: {
avatar: 'pohon:size-8 text-base',
title: 'text-sm',
description: 'text-xs'
},
sm: {
avatar: 'pohon:size-9 text-lg',
title: 'text-sm',
description: 'text-xs'
},
md: {
avatar: 'pohon:size-10 text-xl',
title: 'text-base',
description: 'text-sm'
},
lg: {
avatar: 'pohon:size-11 text-[22px]',
title: 'text-base',
description: 'text-sm'
},
xl: {
avatar: 'pohon:size-12 text-2xl',
title: 'text-lg',
description: 'text-base'
}
},
variant: {
solid: {
root: 'bg-background-inverted',
title: 'color-text-inverted',
description: 'color-text-dimmed'
},
outline: {
root: 'bg-background ring ring-border',
description: 'color-text-muted'
},
soft: {
root: 'bg-background-elevated/50',
description: 'color-text-toned'
},
subtle: {
root: 'bg-background-elevated/50 ring ring-border',
description: 'color-text-toned'
},
naked: {
description: 'color-text-muted'
}
},
loading: {
true: {
avatar: '[&>[data-slot=icon]]:animate-spin'
}
}
},
defaultVariants: {
variant: 'outline',
size: 'md'
}
}
}
})
]
})