Empty State β
This library is a work-in-progress. We are releasing it early to gather feedback, but it is not ready for production.
A versatile component for displaying empty states, placeholder content, and messaging when no data is available. Helps guide users toward meaningful actions and provides clear context about why content is missing.
Examples β
Live Preview
Basic Empty State
Simple empty state with title and subtitle
With Action Button
Include a primary action button using the buttonText prop
Size Variants
Choose from small, medium, or large sizes to fit your layout
Bordered & Rounded
Add visual separation with borders and rounded corners
Custom Icons
Provide custom icons that match your context using the icon slot
Custom Actions
Use the actions slot for complete control over buttons and interactions
Real-World Examples
Common patterns for shopping cart, inbox, dashboard, and error states
State-Specific Variants
Empty states for different scenarios with appropriate icons and actions
Compact Size Showcase
Small empty states work great in sidebars, cards, or dense layouts
CSS Shadow Parts Customization
Use CSS Shadow Parts to customize appearance: ::part(ag-empty-state), ::part(ag-icon), ::part(ag-title), ::part(ag-subtitle), ::part(ag-actions)
Minimal Empty States
Simple empty states without icons for clean, understated designs
Large Welcome States
Spacious layouts perfect for onboarding and full-page empty states
Multiple Button Variants
Showcase different button styles in custom actions
Button Size Variations
Match button sizes to your empty state size for visual harmony
View Vue Code
<template>
<section>
<div class="mbe4">
<h2>Basic Empty State</h2>
<p class="mbs2 mbe3">Simple empty state with title and subtitle</p>
</div>
<VueEmptyState
title="No items found"
subtitle="Get started by creating your first item"
class="mbe6"
/>
<div class="mbe4">
<h2>With Action Button</h2>
<p class="mbs2 mbe3">Include a primary action button using the buttonText prop</p>
</div>
<VueEmptyState
title="No projects yet"
subtitle="Create your first project to get started"
buttonText="New Project"
class="mbe6 empty-state-with-button"
/>
<div class="mbe4">
<h2>Size Variants</h2>
<p class="mbs2 mbe3">Choose from small, medium, or large sizes to fit your layout</p>
</div>
<div class="mbe6">
<VueEmptyState
title="No notifications"
subtitle="You're all caught up!"
size="sm"
class="mbe4"
/>
<VueEmptyState
title="No activity"
subtitle="Recent activity will appear here"
size="md"
class="mbe4"
/>
<VueEmptyState
title="Welcome!"
subtitle="Start your journey here"
buttonText="Get Started"
size="lg"
class="empty-state-with-button"
/>
</div>
<div class="mbe4">
<h2>Bordered & Rounded</h2>
<p class="mbs2 mbe3">Add visual separation with borders and rounded corners</p>
</div>
<div class="mbe6">
<VueEmptyState
title="No data available"
subtitle="Upload a file to see your data"
buttonText="Upload File"
:bordered="true"
class="empty-state-with-button mbe4"
/>
<VueEmptyState
title="Empty folder"
subtitle="This folder contains no files yet"
buttonText="Add Files"
:rounded="true"
class="empty-state-with-button mbe4"
/>
<VueEmptyState
title="No messages"
subtitle="Send your first message"
buttonText="New Message"
:bordered="true"
:rounded="true"
class="empty-state-with-button"
/>
</div>
<div class="mbe4">
<h2>Custom Icons</h2>
<p class="mbs2 mbe3">Provide custom icons that match your context using the icon slot</p>
</div>
<div class="mbe6">
<VueEmptyState
title="No search results"
subtitle="Try different keywords or remove filters"
:rounded="true"
size="md"
class="mbe4"
>
<template #icon>
<SearchX
:size="40"
color="#999"
/>
</template>
</VueEmptyState>
<VueEmptyState
title="No files"
subtitle="Drag and drop files here"
size="md"
:bordered="true"
:rounded="true"
class="mbe4"
>
<template #icon>
<Folder
:size="56"
color="#999"
/>
</template>
</VueEmptyState>
<VueEmptyState
title="No favorites yet"
subtitle="Mark items as favorites to see them here"
:rounded="true"
size="md"
>
<template #icon>
<Heart
:size="32"
color="#ef4444"
/>
</template>
</VueEmptyState>
</div>
<div class="mbe4">
<h2>Custom Actions</h2>
<p class="mbs2 mbe3">Use the actions slot for complete control over buttons and interactions</p>
</div>
<div class="mbe6">
<VueEmptyState
title="No team members"
subtitle="Invite people to collaborate"
size="md"
:bordered="true"
:rounded="true"
class="mbe4"
>
<template #actions>
<div style="display: flex; gap: 0.5rem; justify-content: center; flex-wrap: wrap;">
<VueButton
variant="primary"
size="sm"
shape="rounded"
>
<Mail :size="16" />
<span style="margin-inline-start: 4px;">Invite Members</span>
</VueButton>
<VueButton
variant="secondary"
size="sm"
shape="rounded"
>
Learn More
</VueButton>
</div>
</template>
</VueEmptyState>
<VueEmptyState
title="Start your journey"
subtitle="Choose how you want to begin"
size="md"
:bordered="true"
:rounded="true"
>
<template #icon>
<Folder
:size="56"
color="#999"
/>
</template>
<template #actions>
<div style="display: flex; gap: 0.5rem; flex-wrap: wrap; justify-content: center;">
<VueButton
variant="primary"
size="sm"
shape="rounded"
:isBordered="true"
>
<Plus :size="16" />
<span style="margin-inline-start: 4px;">Create New</span>
</VueButton>
<VueButton
variant="primary"
size="sm"
shape="rounded"
:isBordered="true"
>
<Upload :size="16" />
<span style="margin-inline-start: 4px;">Import Data</span>
</VueButton>
<VueButton
variant="primary"
size="sm"
shape="rounded"
:isBordered="true"
>
<FileText :size="16" />
<span style="margin-inline-start: 4px;">Use Template</span>
</VueButton>
</div>
</template>
</VueEmptyState>
</div>
<div class="mbe4">
<h2>Real-World Examples</h2>
<p class="mbs2 mbe3">Common patterns for shopping cart, inbox, dashboard, and error states</p>
</div>
<div class="mbe6">
<!-- Shopping Cart -->
<VueEmptyState
title="Your cart is empty"
subtitle="Add items to see them here"
size="md"
:bordered="true"
:rounded="true"
class="mbe4"
>
<template #icon>
<ShoppingCart
:size="48"
color="#999"
/>
</template>
<template #actions>
<VueButton
variant="primary"
shape="rounded"
size="md"
>
Continue Shopping
<ArrowRight
:size="16"
style="margin-inline-start: 4px;"
/>
</VueButton>
</template>
</VueEmptyState>
<!-- Inbox Zero -->
<VueEmptyState
title="Inbox Zero! π"
subtitle="All caught up. No new messages."
size="md"
:rounded="true"
class="mbe4"
>
<template #icon>
<Inbox
:size="40"
color="#10b981"
/>
</template>
</VueEmptyState>
<!-- Dashboard -->
<VueEmptyState
title="No analytics data yet"
subtitle="Data will appear once you start tracking"
:bordered="true"
:rounded="true"
class="mbe4"
>
<template #icon>
<BarChart3
:size="56"
color="#6366f1"
/>
</template>
<template #actions>
<VueButton
:isGhost="true"
size="md"
>
View Documentation
</VueButton>
</template>
</VueEmptyState>
<!-- Error State -->
<VueEmptyState
title="Something went wrong"
subtitle="Please try again or contact support"
size="md"
:rounded="true"
:bordered="true"
>
<template #icon>
<AlertCircle
:size="48"
color="#ef4444"
/>
</template>
<template #actions>
<div style="display: flex; gap: 0.5rem; justify-content: center; flex-wrap: wrap;">
<VueButton
variant="danger"
shape="rounded"
:isBordered="true"
size="md"
>
Try Again
</VueButton>
<VueButton
variant="secondary"
shape="rounded"
:isBordered="true"
size="md"
>
<span style="margin-inline-end: 0.125rem;">Contact Support</span>
<Send :size="16" />
</VueButton>
</div>
</template>
</VueEmptyState>
</div>
<div class="mbe4">
<h2>State-Specific Variants</h2>
<p class="mbs2 mbe3">Empty states for different scenarios with appropriate icons and actions</p>
</div>
<div class="mbe6">
<!-- No Results -->
<VueEmptyState
title="No matches found"
subtitle="Try adjusting your filters or search"
size="md"
:bordered="true"
class="mbe4"
>
<template #icon>
<SearchX
:size="40"
color="#999"
/>
</template>
<template #actions>
<VueButton
variant="secondary"
shape="rounded"
size="sm"
@click="handleClearFilters"
>
Clear Filters
</VueButton>
</template>
</VueEmptyState>
<!-- Warning State -->
<VueEmptyState
title="Action Required"
subtitle="This action cannot be undone"
size="md"
:bordered="true"
:rounded="true"
class="mbe4"
>
<template #icon>
<AlertCircle
:size="48"
color="#f59e0b"
/>
</template>
<template #actions>
<div style="display: flex; gap: 0.5rem; justify-content: center; flex-wrap: wrap;">
<VueButton
variant="warning"
size="md"
>
Proceed Anyway
</VueButton>
<VueButton
variant="secondary"
size="md"
>
Cancel
</VueButton>
</div>
</template>
</VueEmptyState>
<!-- Access Denied -->
<VueEmptyState
title="Access Required"
subtitle="You don't have permission to view this"
size="md"
:bordered="true"
>
<template #icon>
<Lock
:size="48"
color="#999"
/>
</template>
<template #actions>
<VueButton
variant="primary"
shape="rounded"
size="md"
>
Request Access
</VueButton>
</template>
</VueEmptyState>
</div>
<div class="mbe4">
<h2>Compact Size Showcase</h2>
<p class="mbs2 mbe3">Small empty states work great in sidebars, cards, or dense layouts</p>
</div>
<div
style="display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: var(--ag-space-4);"
class="mbe6"
>
<VueEmptyState
title="No tasks"
subtitle="Create a task to get started"
buttonText="New Task"
size="sm"
:bordered="true"
:rounded="true"
class="empty-state-with-button"
/>
<VueEmptyState
title="No events"
subtitle="Schedule your first event"
buttonText="Add Event"
size="sm"
:bordered="true"
:rounded="true"
class="empty-state-with-button"
/>
<VueEmptyState
title="No contacts"
subtitle="Add contacts to your list"
buttonText="Add Contact"
size="sm"
:bordered="true"
:rounded="true"
class="empty-state-with-button"
/>
<VueEmptyState
title="No notes"
subtitle="Jot down your first note"
buttonText="Create Note"
size="sm"
:bordered="true"
:rounded="true"
class="empty-state-with-button"
/>
</div>
<div class="mbe4">
<h2>CSS Shadow Parts Customization</h2>
<p class="mbs2 mbe3">
Use CSS Shadow Parts to customize appearance:
<code>::part(ag-empty-state)</code>,
<code>::part(ag-icon)</code>,
<code>::part(ag-title)</code>,
<code>::part(ag-subtitle)</code>,
<code>::part(ag-actions)</code>
</p>
</div>
<div class="mbe6">
<!-- Colorful Accent -->
<VueEmptyState
title="Colorful Accent"
subtitle="Icon with background circle"
buttonText="Start Now"
:bordered="true"
class="custom-empty-state-3"
>
<template #icon>
<Rocket
:size="48"
color="#f59e0b"
/>
</template>
</VueEmptyState>
</div>
<div class="mbe4">
<h2>Minimal Empty States</h2>
<p class="mbs2 mbe3">Simple empty states without icons for clean, understated designs</p>
</div>
<div class="mbe6">
<VueEmptyState
title="Nothing here yet"
size="sm"
:rounded="true"
class="mbe4"
/>
<VueEmptyState
title="No activity"
subtitle="Recent activity will appear here"
size="md"
:rounded="true"
class="mbe4"
/>
<VueEmptyState
title="Empty"
:rounded="true"
size="md"
/>
</div>
<div class="mbe4">
<h2>Large Welcome States</h2>
<p class="mbs2 mbe3">Spacious layouts perfect for onboarding and full-page empty states</p>
</div>
<VueEmptyState
title="Welcome to Your Dashboard"
subtitle="Add some content and you'll see it appear here. You'll be able to manage projects and collaborate with your team."
buttonText="Get Started"
size="lg"
:bordered="true"
:rounded="true"
class="empty-state-with-button mbe6"
>
<template #icon>
<Sparkles
:size="48"
color="#6366f1"
/>
</template>
</VueEmptyState>
<div class="mbe4">
<h2>Multiple Button Variants</h2>
<p class="mbs2 mbe3">Showcase different button styles in custom actions</p>
</div>
<VueEmptyState
title="Choose your action"
subtitle="Different button styles for different purposes"
size="md"
:bordered="true"
:rounded="true"
class="mbe6"
>
<template #icon>
<AlertCircle
:size="40"
color="#999"
/>
</template>
<template #actions>
<div style="display: flex; gap: 0.5rem; flex-wrap: wrap; justify-content: center;">
<VueButton
variant="primary"
size="sm"
shape="rounded"
>
Primary Action
</VueButton>
<VueButton
variant="secondary"
size="sm"
shape="rounded"
:isBordered="true"
>
Secondary
</VueButton>
<VueButton
size="sm"
shape="rounded"
:isBordered="true"
>
Default
</VueButton>
</div>
</template>
</VueEmptyState>
<div class="mbe4">
<h2>Button Size Variations</h2>
<p class="mbs2 mbe3">Match button sizes to your empty state size for visual harmony</p>
</div>
<VueEmptyState
title="Different Button Sizes"
subtitle="Choose the right size for your layout"
size="md"
:bordered="true"
:rounded="true"
class="mbe6"
>
<template #icon>
<Folder
:size="48"
color="#999"
/>
</template>
<template #actions>
<div style="display: flex; gap: 0.5rem; align-items: center; justify-content: center; flex-wrap: wrap;">
<VueButton
variant="primary"
size="sm"
shape="rounded"
:isBordered="true"
>
<Mic :size="14" />
<span style="margin-inline-start: 4px;">Small</span>
</VueButton>
<VueButton
variant="primary"
size="md"
shape="rounded"
:isBordered="true"
>
<Mic :size="18" />
<span style="margin-inline-start: 4px;">Medium</span>
</VueButton>
<VueButton
variant="primary"
size="lg"
shape="rounded"
:isBordered="true"
>
<Mic :size="24" />
<span style="margin-inline-start: 4px;">Large</span>
</VueButton>
</div>
</template>
</VueEmptyState>
</section>
</template>
<script>
import { VueEmptyState } from "agnosticui-core/empty-state/vue";
import VueButton from "agnosticui-core/button/vue";
import {
SearchX,
Folder,
Heart,
Mail,
Plus,
Upload,
FileText,
ShoppingCart,
ArrowRight,
Inbox,
BarChart3,
AlertCircle,
Send,
Lock,
Sparkles,
Zap,
Rocket,
Mic,
} from "lucide-vue-next";
export default {
name: "EmptyStateExamples",
components: {
VueEmptyState,
VueButton,
SearchX,
Folder,
Heart,
Mail,
Plus,
Upload,
FileText,
ShoppingCart,
ArrowRight,
Inbox,
BarChart3,
AlertCircle,
Send,
Lock,
Sparkles,
Zap,
Rocket,
Mic,
},
methods: {
handleClearFilters() {
console.log("Clearing filters...");
},
},
};
</script>
<style scoped>
/* Force empty state components to have proper height */
ag-empty-state {
display: block;
min-height: 200px;
}
ag-empty-state::part(ag-empty-state) {
min-height: 200px;
display: flex;
flex-direction: column;
justify-content: center;
padding: var(--ag-space-6, 2rem) var(--ag-space-4, 1rem);
}
/* Size-specific min heights */
ag-empty-state[size="sm"]::part(ag-empty-state) {
min-height: 180px;
padding: var(--ag-space-5, 1.5rem) var(--ag-space-4, 1rem);
}
ag-empty-state[size="md"]::part(ag-empty-state) {
min-height: 220px;
padding: var(--ag-space-6, 2rem) var(--ag-space-4, 1rem);
}
ag-empty-state[size="lg"]::part(ag-empty-state) {
min-height: 300px;
padding: var(--ag-space-8, 3rem) var(--ag-space-4, 1rem);
}
/* Add spacing for icon */
ag-empty-state::part(ag-icon) {
margin-bottom: var(--ag-space-4, 1.5rem);
}
/* Shared action styles for buttonText prop */
.empty-state-with-button::part(ag-actions) {
display: flex;
justify-content: center;
margin-block-start: var(--ag-space-4);
}
.empty-state-with-button::part(ag-title) {
margin-block-start: var(--ag-space-4);
}
.empty-state-with-button::part(ag-actions-button) {
background: #09090b;
color: white;
border-radius: 0.25rem;
padding: var(--ag-space-4) var(--ag-space-6);
border: none;
font-weight: 600;
cursor: pointer;
transition: all 0.2s;
}
.empty-state-with-button::part(ag-actions-button):hover {
background: #18181b;
transform: translateY(-1px);
}
/* Custom Empty State 3 - Colorful Icon Accent */
.custom-empty-state-3::part(ag-icon) {
color: #f59e0b;
background: rgba(245, 158, 11, 0.1);
border-radius: 50%;
padding: var(--ag-space-4);
}
.custom-empty-state-3::part(ag-title) {
color: var(--ag-text-primary);
font-weight: 700;
}
.custom-empty-state-3::part(ag-actions-button) {
background: #f59e0b;
color: var(--ag-neutral-900);
border: none;
border-radius: 8px;
padding: 0.75rem 1.5rem;
font-weight: 600;
box-shadow: 0 4px 12px rgba(245, 158, 11, 0.3);
}
</style>
Live Preview
View Lit / Web Component Code
import { LitElement, html } from "lit";
import "agnosticui-core/empty-state";
import "agnosticui-core/button";
import "agnosticui-core/icon";
class EmptyStateLitExamples extends LitElement {
// Render in light DOM to access global utility classes
createRenderRoot() {
return this;
}
render() {
return html`
<style>
/* Force empty state components to have proper height */
ag-empty-state {
display: block;
min-height: 200px;
}
ag-empty-state::part(ag-empty-state) {
min-height: 200px;
display: flex;
flex-direction: column;
justify-content: center;
padding: var(--ag-space-6, 2rem) var(--ag-space-4, 1rem);
}
/* Size-specific min heights */
ag-empty-state[size="sm"]::part(ag-empty-state) {
min-height: 180px;
padding: var(--ag-space-5, 1.5rem) var(--ag-space-4, 1rem);
}
ag-empty-state[size="md"]::part(ag-empty-state) {
min-height: 220px;
padding: var(--ag-space-6, 2rem) var(--ag-space-4, 1rem);
}
ag-empty-state[size="lg"]::part(ag-empty-state) {
min-height: 300px;
padding: var(--ag-space-8, 3rem) var(--ag-space-4, 1rem);
}
/* Add spacing for icon */
ag-empty-state::part(ag-icon) {
margin-bottom: var(--ag-space-4, 1.5rem);
}
/* Shared action styles for buttonText prop */
.empty-state-with-button::part(ag-actions) {
display: flex;
justify-content: center;
margin-block-start: var(--ag-space-4);
}
.empty-state-with-button::part(ag-title) {
margin-block-start: var(--ag-space-4);
}
.empty-state-with-button::part(ag-actions-button) {
background: #09090b;
color: white;
border-radius: 0.25rem;
padding: var(--ag-space-4) var(--ag-space-6);
border: none;
font-weight: 600;
cursor: pointer;
transition: all 0.2s;
}
.empty-state-with-button::part(ag-actions-button):hover {
background: #18181b;
transform: translateY(-1px);
}
/* Custom Empty State 3 - Colorful Icon Accent */
.custom-empty-state-3::part(ag-icon) {
color: #f59e0b;
background: rgba(245, 158, 11, 0.1);
border-radius: 50%;
padding: var(--ag-space-4);
}
.custom-empty-state-3::part(ag-title) {
color: var(--ag-text-primary);
font-weight: 700;
}
.custom-empty-state-3::part(ag-actions-button) {
background: #f59e0b;
color: var(--ag-neutral-900);
border: none;
border-radius: 8px;
padding: 0.75rem 1.5rem;
font-weight: 600;
box-shadow: 0 4px 12px rgba(245, 158, 11, 0.3);
}
</style>
<section>
<div class="mbe4">
<h2>Basic Empty State</h2>
<p class="mbs2 mbe3">Simple empty state with title and subtitle</p>
</div>
<ag-empty-state
title="No items found"
subtitle="Get started by creating your first item"
class="mbe6"
></ag-empty-state>
<div class="mbe4">
<h2>With Action Button</h2>
<p class="mbs2 mbe3">
Include a primary action button using the buttonText prop
</p>
</div>
<ag-empty-state
title="No projects yet"
subtitle="Create your first project to get started"
buttonText="New Project"
class="mbe6 empty-state-with-button"
></ag-empty-state>
<div class="mbe4">
<h2>Size Variants</h2>
<p class="mbs2 mbe3">
Choose from small, medium, or large sizes to fit your layout
</p>
</div>
<div class="mbe6">
<ag-empty-state
title="No notifications"
subtitle="You're all caught up!"
size="sm"
class="mbe4"
></ag-empty-state>
<ag-empty-state
title="No activity"
subtitle="Recent activity will appear here"
size="md"
class="mbe4"
></ag-empty-state>
<ag-empty-state
title="Welcome!"
subtitle="Start your journey here"
buttonText="Get Started"
size="lg"
class="empty-state-with-button"
></ag-empty-state>
</div>
<div class="mbe4">
<h2>Bordered & Rounded</h2>
<p class="mbs2 mbe3">
Add visual separation with borders and rounded corners
</p>
</div>
<div class="mbe6">
<ag-empty-state
title="No data available"
subtitle="Upload a file to see your data"
buttonText="Upload File"
isBordered
class="empty-state-with-button mbe4"
></ag-empty-state>
<ag-empty-state
title="Empty folder"
subtitle="This folder contains no files yet"
buttonText="Add Files"
isRounded
class="empty-state-with-button mbe4"
></ag-empty-state>
<ag-empty-state
title="No messages"
subtitle="Send your first message"
buttonText="New Message"
isBordered
isRounded
class="empty-state-with-button"
></ag-empty-state>
</div>
<div class="mbe4">
<h2>Custom Icons</h2>
<p class="mbs2 mbe3">
Provide custom icons that match your context using the icon slot
</p>
</div>
<div class="mbe6">
<ag-empty-state
title="No search results"
subtitle="Try different keywords or remove filters"
isRounded
size="md"
class="mbe4"
>
<ag-icon slot="icon" size="40" no-fill>
<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 24 24" fill="#999" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-search-x">
<path d="m13.5 8.5-5 5"/><path d="m8.5 8.5 5 5"/><circle cx="11" cy="11" r="8"/><path d="m21 21-4.3-4.3"/>
</svg>
</ag-icon>
</ag-empty-state>
<ag-empty-state
title="No files"
subtitle="Drag and drop files here"
size="md"
isBordered
isRounded
class="mbe4"
>
<ag-icon slot="icon" size="56" no-fill>
<svg xmlns="http://www.w3.org/2000/svg" width="56" height="56" viewBox="0 0 24 24" fill="#999" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-folder">
<path d="M4 20h16a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.93a2 2 0 0 1-1.66-.9l-.82-1.2A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13c0 1.1.9 2 2 2Z"/>
</svg>
</ag-icon>
</ag-empty-state>
<ag-empty-state
title="No favorites yet"
subtitle="Mark items as favorites to see them here"
isRounded
size="md"
>
<ag-icon slot="icon" size="32" no-fill>
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" fill="#ef4444" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-heart">
<path d="M19 14c1.49-1.46 3-3.21 3-5.5A5.5 5.5 0 0 0 16.5 3c-1.76 0-3 .5-4.5 2-1.5-1.5-2.74-2-4.5-2A5.5 5.5 0 0 0 2 8.5c0 2.3 1.5 4.05 3 5.5l7 7Z"/>
</svg>
</ag-icon>
</ag-empty-state>
</div>
<div class="mbe4">
<h2>Custom Actions</h2>
<p class="mbs2 mbe3">
Use the actions slot for complete control over buttons and
interactions
</p>
</div>
<div class="mbe6">
<ag-empty-state
title="No team members"
subtitle="Invite people to collaborate"
size="md"
isBordered
isRounded
class="mbe4"
>
<div
slot="actions"
style="display: flex; gap: 0.5rem; justify-content: center; flex-wrap: wrap;"
>
<ag-button variant="primary" size="sm" shape="rounded">
<ag-icon size="16" no-fill>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-mail">
<rect width="20" height="16" x="2" y="4" rx="2"/><path d="m22 7-8.97 5.7a1.94 1.94 0 0 1-2.06 0L2 7"/>
</svg>
</ag-icon>
<span style="margin-inline-start: 4px;">Invite Members</span>
</ag-button>
<ag-button variant="secondary" size="sm" shape="rounded">
Learn More
</ag-button>
</div>
</ag-empty-state>
<ag-empty-state
title="Start your journey"
subtitle="Choose how you want to begin"
size="md"
isBordered
isRounded
>
<ag-icon slot="icon" size="56" no-fill>
<svg xmlns="http://www.w3.org/2000/svg" width="56" height="56" viewBox="0 0 24 24" fill="#999" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-folder">
<path d="M4 20h16a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.93a2 2 0 0 1-1.66-.9l-.82-1.2A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13c0 1.1.9 2 2 2Z"/>
</svg>
</ag-icon>
<div
slot="actions"
style="display: flex; gap: 0.5rem; flex-wrap: wrap; justify-content: center;"
>
<ag-button variant="primary" size="sm" shape="rounded" isBordered>
<ag-icon size="16" no-fill>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-plus">
<path d="M5 12h14"/><path d="M12 5v14"/>
</svg>
</ag-icon>
<span style="margin-inline-start: 4px;">Create New</span>
</ag-button>
<ag-button variant="primary" size="sm" shape="rounded" isBordered>
<ag-icon size="16" no-fill>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-upload">
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="17 8 12 3 7 8"/><line x1="12" x2="12" y1="3" y2="15"/>
</svg>
</ag-icon>
<span style="margin-inline-start: 4px;">Import Data</span>
</ag-button>
<ag-button variant="primary" size="sm" shape="rounded" isBordered>
<ag-icon size="16" no-fill>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-file-text">
<path d="M14.5 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7.5L14.5 2z"/><polyline points="14 2 14 8 20 8"/><line x1="16" x2="8" y1="13" y2="13"/><line x1="16" x2="8" y1="17" y2="17"/><line x1="10" x2="8" y1="9" y2="9"/>
</svg>
</ag-icon>
<span style="margin-inline-start: 4px;">Use Template</span>
</ag-button>
</div>
</ag-empty-state>
</div>
<div class="mbe4">
<h2>Real-World Examples</h2>
<p class="mbs2 mbe3">
Common patterns for shopping cart, inbox, dashboard, and error
states
</p>
</div>
<div class="mbe6">
<!-- Shopping Cart -->
<ag-empty-state
title="Your cart is empty"
subtitle="Add items to see them here"
size="md"
isBordered
isRounded
class="mbe4"
>
<ag-icon slot="icon" size="48" no-fill>
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 24 24" fill="#999" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-shopping-cart">
<circle cx="8" cy="21" r="1"/><circle cx="19" cy="21" r="1"/><path d="M2.05 2.05h2l2.66 12.42a2 2 0 0 0 2 1.58h9.78a2 2 0 0 0 1.95-1.57l1.65-7.43H5.16"/>
</svg>
</ag-icon>
<div slot="actions">
<ag-button variant="primary" shape="rounded" size="md">
Continue Shopping
<ag-icon size="16" no-fill style="margin-inline-start: 4px;">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-arrow-right">
<path d="M5 12h14"/><path d="m12 5 7 7-7 7"/>
</svg>
</ag-icon>
</ag-button>
</div>
</ag-empty-state>
<!-- Inbox Zero -->
<ag-empty-state
title="Inbox Zero! π"
subtitle="All caught up. No new messages."
size="md"
isRounded
class="mbe4"
>
<ag-icon slot="icon" size="40" no-fill>
<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 24 24" fill="#10b981" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-inbox">
<path d="M22 12h-6l-2 3h-4l-2-3H2"/><path d="M5.45 5.11 2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.45-6.89A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z"/>
</svg>
</ag-icon>
</ag-empty-state>
<!-- Dashboard -->
<ag-empty-state
title="No analytics data yet"
subtitle="Data will appear once you start tracking"
isBordered
isRounded
class="mbe4"
>
<ag-icon slot="icon" size="56" no-fill>
<svg xmlns="http://www.w3.org/2000/svg" width="56" height="56" viewBox="0 0 24 24" fill="#6366f1" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-bar-chart-3">
<path d="M3 3v18h18"/><path d="M18 17V9"/><path d="M13 17V5"/><path d="M8 17v-3"/>
</svg>
</ag-icon>
<div slot="actions">
<ag-button isGhost size="md"> View Documentation </ag-button>
</div>
</ag-empty-state>
<!-- Error State -->
<ag-empty-state
title="Something went wrong"
subtitle="Please try again or contact support"
size="md"
isRounded
isBordered
>
<ag-icon slot="icon" size="48" no-fill>
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 24 24" fill="#ef4444" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-alert-circle">
<circle cx="12" cy="12" r="10"/><line x1="12" x2="12" y1="8" y2="12"/><line x1="12" x2="12.01" y1="16" y2="16"/>
</svg>
</ag-icon>
<div
slot="actions"
style="display: flex; gap: 0.5rem; justify-content: center; flex-wrap: wrap;"
>
<ag-button
variant="danger"
shape="rounded"
isBordered
size="md"
>
Try Again
</ag-button>
<ag-button
variant="secondary"
shape="rounded"
isBordered
size="md"
>
<span style="margin-inline-end: 0.125rem;"
>Contact Support</span
>
<ag-icon size="16" no-fill>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-send">
<path d="m22 2-7 20-4-9-9-4Z"/><path d="M22 2 11 13"/>
</svg>
</ag-icon>
</ag-button>
</div>
</ag-empty-state>
</div>
<div class="mbe4">
<h2>State-Specific Variants</h2>
<p class="mbs2 mbe3">
Empty states for different scenarios with appropriate icons and
actions
</p>
</div>
<div class="mbe6">
<!-- No Results -->
<ag-empty-state
title="No matches found"
subtitle="Try adjusting your filters or search"
size="md"
isBordered
class="mbe4"
>
<ag-icon slot="icon" size="40" no-fill>
<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 24 24" fill="#999" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-search-x">
<path d="m13.5 8.5-5 5"/><path d="m8.5 8.5 5 5"/><circle cx="11" cy="11" r="8"/><path d="m21 21-4.3-4.3"/>
</svg>
</ag-icon>
<div slot="actions">
<ag-button
variant="secondary"
shape="rounded"
size="sm"
@click=${() => console.log("clicked")}
>
Clear Filters
</ag-button>
</div>
</ag-empty-state>
<!-- Warning State -->
<ag-empty-state
title="Action Required"
subtitle="This action cannot be undone"
size="md"
isBordered
isRounded
class="mbe4"
>
<ag-icon slot="icon" size="48" no-fill>
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 24 24" fill="#f59e0b" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-alert-circle">
<circle cx="12" cy="12" r="10"/><line x1="12" x2="12" y1="8" y2="12"/><line x1="12" x2="12.01" y1="16" y2="16"/>
</svg>
</ag-icon>
<div
slot="actions"
style="display: flex; gap: 0.5rem; justify-content: center; flex-wrap: wrap;"
>
<ag-button variant="warning" size="md">
Proceed Anyway
</ag-button>
<ag-button variant="secondary" size="md"> Cancel </ag-button>
</div>
</ag-empty-state>
<!-- Access Denied -->
<ag-empty-state
title="Access Required"
subtitle="You don't have permission to view this"
size="md"
isBordered
>
<ag-icon slot="icon" size="48" no-fill>
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 24 24" fill="#999" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-lock">
<rect width="18" height="11" x="3" y="11" rx="2" ry="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/>
</svg>
</ag-icon>
<div slot="actions">
<ag-button variant="primary" shape="rounded" size="md">
Request Access
</ag-button>
</div>
</ag-empty-state>
</div>
<div class="mbe4">
<h2>Compact Size Showcase</h2>
<p class="mbs2 mbe3">
Small empty states work great in sidebars, cards, or dense layouts
</p>
</div>
<div
style="display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: var(--ag-space-4);"
class="mbe6"
>
<ag-empty-state
title="No tasks"
subtitle="Create a task to get started"
buttonText="New Task"
size="sm"
isBordered
isRounded
class="empty-state-with-button"
></ag-empty-state>
<ag-empty-state
title="No events"
subtitle="Schedule your first event"
buttonText="Add Event"
size="sm"
isBordered
isRounded
class="empty-state-with-button"
></ag-empty-state>
<ag-empty-state
title="No contacts"
subtitle="Add contacts to your list"
buttonText="Add Contact"
size="sm"
isBordered
isRounded
class="empty-state-with-button"
></ag-empty-state>
<ag-empty-state
title="No notes"
subtitle="Jot down your first note"
buttonText="Create Note"
size="sm"
isBordered
isRounded
class="empty-state-with-button"
></ag-empty-state>
</div>
<div class="mbe4">
<h2>CSS Shadow Parts Customization</h2>
<p class="mbs2 mbe3">
Use CSS Shadow Parts to customize appearance:
<code>::part(ag-empty-state)</code>,
<code>::part(ag-icon)</code>, <code>::part(ag-title)</code>,
<code>::part(ag-subtitle)</code>, <code>::part(ag-actions)</code>
</p>
</div>
<div class="mbe6">
<!-- Colorful Accent -->
<ag-empty-state
title="Colorful Accent"
subtitle="Icon with background circle"
buttonText="Start Now"
isBordered
class="custom-empty-state-3"
>
<ag-icon slot="icon" size="48" no-fill>
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 24 24" fill="#f59e0b" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-rocket">
<path d="M4.5 16.5c-1.5 1.26-2 5-2 5s3.74-.5 5-2c.71-.84.7-2.3.09-3.1a2.18 2.18 0 0 0-3.11-.1z"/><path d="m12 15-3-3a22 22 0 0 1 2-3.95A12.88 12.88 0 0 1 22 2c0 2.72-.78 7.5-6 11a22.35 22.35 0 0 1-4 2z"/><path d="M9 12H4s.55-3.03 2-4c1.62-1.08 5 0 5 0"/><path d="M12 15v5s3.03-.55 4-2c1.08-1.62 0-5 0-5"/>
</svg>
</ag-icon>
</ag-empty-state>
</div>
<div class="mbe4">
<h2>Minimal Empty States</h2>
<p class="mbs2 mbe3">
Simple empty states without icons for clean, understated designs
</p>
</div>
<div class="mbe6">
<ag-empty-state
title="Nothing here yet"
size="sm"
isRounded
class="mbe4"
></ag-empty-state>
<ag-empty-state
title="No activity"
subtitle="Recent activity will appear here"
size="md"
isRounded
class="mbe4"
></ag-empty-state>
<ag-empty-state
title="Empty"
isRounded
size="md"
></ag-empty-state>
</div>
<div class="mbe4">
<h2>Large Welcome States</h2>
<p class="mbs2 mbe3">
Spacious layouts perfect for onboarding and full-page empty states
</p>
</div>
<ag-empty-state
title="Welcome to Your Dashboard"
subtitle="Add some content and you'll see it appear here. You'll be able to manage projects and collaborate with your team."
buttonText="Get Started"
size="lg"
isBordered
isRounded
class="empty-state-with-button mbe6"
>
<ag-icon slot="icon" size="48" no-fill>
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 24 24" fill="#6366f1" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-sparkles">
<path d="m12 3-1.912 5.813a2 2 0 0 1-1.275 1.275L3 12l5.813 1.912a2 2 0 0 1 1.275 1.275L12 21l1.912-5.813a2 2 0 0 1 1.275-1.275L21 12l-5.813-1.912a2 2 0 0 1-1.275-1.275L12 3Z"/><path d="M5 3v4"/><path d="M19 17v4"/><path d="M3 5h4"/><path d="M17 19h4"/>
</svg>
</ag-icon>
</ag-empty-state>
<div class="mbe4">
<h2>Multiple Button Variants</h2>
<p class="mbs2 mbe3">
Showcase different button styles in custom actions
</p>
</div>
<ag-empty-state
title="Choose your action"
subtitle="Different button styles for different purposes"
size="md"
isBordered
isRounded
class="mbe6"
>
<ag-icon slot="icon" size="40" no-fill>
<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 24 24" fill="#999" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-alert-circle">
<circle cx="12" cy="12" r="10"/><line x1="12" x2="12" y1="8" y2="12"/><line x1="12" x2="12.01" y1="16" y2="16"/>
</svg>
</ag-icon>
<div
slot="actions"
style="display: flex; gap: 0.5rem; flex-wrap: wrap; justify-content: center;"
>
<ag-button variant="primary" size="sm" shape="rounded">
Primary Action
</ag-button>
<ag-button
variant="secondary"
size="sm"
shape="rounded"
isBordered
>
Secondary
</ag-button>
<ag-button size="sm" shape="rounded" isBordered>
Default
</ag-button>
</div>
</ag-empty-state>
<div class="mbe4">
<h2>Button Size Variations</h2>
<p class="mbs2 mbe3">
Match button sizes to your empty state size for visual harmony
</p>
</div>
<ag-empty-state
title="Different Button Sizes"
subtitle="Choose the right size for your layout"
size="md"
isBordered
isRounded
class="mbe6"
>
<ag-icon slot="icon" size="48" no-fill>
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 24 24" fill="#999" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-folder">
<path d="M4 20h16a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.93a2 2 0 0 1-1.66-.9l-.82-1.2A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13c0 1.1.9 2 2 2Z"/>
</svg>
</ag-icon>
<div
slot="actions"
style="display: flex; gap: 0.5rem; align-items: center; justify-content: center; flex-wrap: wrap;"
>
<ag-button variant="primary" size="sm" shape="rounded" isBordered>
<ag-icon size="14" no-fill>
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-mic">
<path d="M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3Z"/><path d="M19 10v2a7 7 0 0 1-14 0v-2"/><line x1="12" x2="12" y1="19" y2="22"/>
</svg>
</ag-icon>
<span style="margin-inline-start: 4px;">Small</span>
</ag-button>
<ag-button variant="primary" size="md" shape="rounded" isBordered>
<ag-icon size="18" no-fill>
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-mic">
<path d="M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3Z"/><path d="M19 10v2a7 7 0 0 1-14 0v-2"/><line x1="12" x2="12" y1="19" y2="22"/>
</svg>
</ag-icon>
<span style="margin-inline-start: 4px;">Medium</span>
</ag-button>
<ag-button variant="primary" size="lg" shape="rounded" isBordered>
<ag-icon size="24" no-fill>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-mic">
<path d="M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3Z"/><path d="M19 10v2a7 7 0 0 1-14 0v-2"/><line x1="12" x2="12" y1="19" y2="22"/>
</svg>
</ag-icon>
<span style="margin-inline-start: 4px;">Large</span>
</ag-button>
</div>
</ag-empty-state>
</section>
`;
}
}
customElements.define("empty-state-lit-examples", EmptyStateLitExamples);
Interactive Preview: Click the "Open in StackBlitz" button below to see this example running live in an interactive playground.
View React Code
import {
SearchX,
Folder,
Heart,
Mail,
Plus,
Upload,
FileText,
ShoppingCart,
ArrowRight,
Inbox,
BarChart3,
AlertCircle,
Send,
Lock,
Sparkles,
Zap,
Rocket,
Mic,
} from "lucide-react";
import { ReactEmptyState } from "agnosticui-core/empty-state/react";
import { ReactButton } from "agnosticui-core/button/react";
const EmptyStateExamples = () => {
const handleClearFilters = () => {
console.log("Clearing filters...");
};
return (
<section>
<div className="mbe4">
<h2>Basic Empty State</h2>
<p className="mbs2 mbe3">Simple empty state with title and subtitle</p>
</div>
<ReactEmptyState
title="No items found"
subtitle="Get started by creating your first item"
className="mbe6"
/>
<div className="mbe4">
<h2>With Action Button</h2>
<p className="mbs2 mbe3">Include a primary action button using the buttonText prop</p>
</div>
<ReactEmptyState
title="No projects yet"
subtitle="Create your first project to get started"
buttonText="New Project"
className="mbe6 empty-state-with-button"
/>
<div className="mbe4">
<h2>Size Variants</h2>
<p className="mbs2 mbe3">Choose from small, medium, or large sizes to fit your layout</p>
</div>
<div className="mbe6">
<ReactEmptyState
title="No notifications"
subtitle="You're all caught up!"
size="sm"
className="mbe4"
/>
<ReactEmptyState
title="No activity"
subtitle="Recent activity will appear here"
size="md"
className="mbe4"
/>
<ReactEmptyState
title="Welcome!"
subtitle="Start your journey here"
buttonText="Get Started"
size="lg"
className="empty-state-with-button"
/>
</div>
<div className="mbe4">
<h2>Bordered & Rounded</h2>
<p className="mbs2 mbe3">Add visual separation with borders and rounded corners</p>
</div>
<div className="mbe6">
<ReactEmptyState
title="No data available"
subtitle="Upload a file to see your data"
buttonText="Upload File"
isBordered={true}
className="empty-state-with-button mbe4"
/>
<ReactEmptyState
title="Empty folder"
subtitle="This folder contains no files yet"
buttonText="Add Files"
isRounded={true}
className="empty-state-with-button mbe4"
/>
<ReactEmptyState
title="No messages"
subtitle="Send your first message"
buttonText="New Message"
isBordered={true}
isRounded={true}
className="empty-state-with-button"
/>
</div>
<div className="mbe4">
<h2>Custom Icons</h2>
<p className="mbs2 mbe3">Provide custom icons that match your context using the icon slot</p>
</div>
<div className="mbe6">
<ReactEmptyState
title="No search results"
subtitle="Try different keywords or remove filters"
isRounded={true}
size="md"
className="mbe4"
icon={
<SearchX
size={40}
color="#999"
/>
}
/>
<ReactEmptyState
title="No files"
subtitle="Drag and drop files here"
size="md"
isBordered={true}
isRounded={true}
className="mbe4"
icon={
<Folder
size={56}
color="#999"
/>
}
/>
<ReactEmptyState
title="No favorites yet"
subtitle="Mark items as favorites to see them here"
isRounded={true}
size="md"
icon={
<Heart
size={32}
color="#ef4444"
/>
}
/>
</div>
<div className="mbe4">
<h2>Custom Actions</h2>
<p className="mbs2 mbe3">Use the actions slot for complete control over buttons and interactions</p>
</div>
<div className="mbe6">
<ReactEmptyState
title="No team members"
subtitle="Invite people to collaborate"
size="md"
isBordered={true}
isRounded={true}
className="mbe4"
actions={
<div style={{ display: "flex", gap: "0.5rem", justifyContent: "center", flexWrap: "wrap" }}>
<ReactButton
variant="primary"
size="sm"
shape="rounded"
>
<Mail size={16} />
<span style={{ marginInlineStart: "4px" }}>Invite Members</span>
</ReactButton>
<ReactButton
variant="secondary"
size="sm"
shape="rounded"
>
Learn More
</ReactButton>
</div>
}
/>
<ReactEmptyState
title="Start your journey"
subtitle="Choose how you want to begin"
size="md"
isBordered={true}
isRounded={true}
icon={
<Folder
size={56}
color="#999"
/>
}
actions={
<div style={{ display: "flex", gap: "0.5rem", flexWrap: "wrap", justifyContent: "center" }}>
<ReactButton
variant="primary"
size="sm"
shape="rounded"
isBordered={true}
>
<Plus size={16} />
<span style={{ marginInlineStart: "4px" }}>Create New</span>
</ReactButton>
<ReactButton
variant="primary"
size="sm"
shape="rounded"
isBordered={true}
>
<Upload size={16} />
<span style={{ marginInlineStart: "4px" }}>Import Data</span>
</ReactButton>
<ReactButton
variant="primary"
size="sm"
shape="rounded"
isBordered={true}
>
<FileText size={16} />
<span style={{ marginInlineStart: "4px" }}>Use Template</span>
</ReactButton>
</div>
}
/>
</div>
<div className="mbe4">
<h2>Real-World Examples</h2>
<p className="mbs2 mbe3">Common patterns for shopping cart, inbox, dashboard, and error states</p>
</div>
<div className="mbe6">
{/* Shopping Cart */}
<ReactEmptyState
title="Your cart is empty"
subtitle="Add items to see them here"
size="md"
isBordered={true}
isRounded={true}
className="mbe4"
icon={
<ShoppingCart
size={48}
color="#999"
/>
}
actions={
<ReactButton
variant="primary"
shape="rounded"
size="md"
>
Continue Shopping
<ArrowRight
size={16}
style={{ marginInlineStart: "4px" }}
/>
</ReactButton>
}
/>
{/* Inbox Zero */}
<ReactEmptyState
title="Inbox Zero! π"
subtitle="All caught up. No new messages."
size="md"
isRounded={true}
className="mbe4"
icon={
<Inbox
size={40}
color="#10b981"
/>
}
/>
{/* Dashboard */}
<ReactEmptyState
title="No analytics data yet"
subtitle="Data will appear once you start tracking"
isBordered={true}
isRounded={true}
className="mbe4"
icon={
<BarChart3
size={56}
color="#6366f1"
/>
}
actions={
<ReactButton
isGhost={true}
size="md"
>
View Documentation
</ReactButton>
}
/>
{/* Error State */}
<ReactEmptyState
title="Something went wrong"
subtitle="Please try again or contact support"
size="md"
isRounded={true}
isBordered={true}
icon={
<AlertCircle
size={48}
color="#ef4444"
/>
}
actions={
<div style={{ display: "flex", gap: "0.5rem", justifyContent: "center", flexWrap: "wrap" }}>
<ReactButton
variant="danger"
shape="rounded"
isBordered={true}
size="md"
>
Try Again
</ReactButton>
<ReactButton
variant="secondary"
shape="rounded"
isBordered={true}
size="md"
>
<span style={{ marginInlineEnd: "0.125rem" }}>Contact Support</span>
<Send size={16} />
</ReactButton>
</div>
}
/>
</div>
<div className="mbe4">
<h2>State-Specific Variants</h2>
<p className="mbs2 mbe3">Empty states for different scenarios with appropriate icons and actions</p>
</div>
<div className="mbe6">
{/* No Results */}
<ReactEmptyState
title="No matches found"
subtitle="Try adjusting your filters or search"
size="md"
isBordered={true}
className="mbe4"
icon={
<SearchX
size={40}
color="#999"
/>
}
actions={
<ReactButton
variant="secondary"
shape="rounded"
size="sm"
onClick={handleClearFilters}
>
Clear Filters
</ReactButton>
}
/>
{/* Warning State */}
<ReactEmptyState
title="Action Required"
subtitle="This action cannot be undone"
size="md"
isBordered={true}
isRounded={true}
className="mbe4"
icon={
<AlertCircle
size={48}
color="#f59e0b"
/>
}
actions={
<div style={{ display: "flex", gap: "0.5rem", justifyContent: "center", flexWrap: "wrap" }}>
<ReactButton
variant="warning"
size="md"
>
Proceed Anyway
</ReactButton>
<ReactButton
variant="secondary"
size="md"
>
Cancel
</ReactButton>
</div>
}
/>
{/* Access Denied */}
<ReactEmptyState
title="Access Required"
subtitle="You don't have permission to view this"
size="md"
isBordered={true}
icon={
<Lock
size={48}
color="#999"
/>
}
actions={
<ReactButton
variant="primary"
shape="rounded"
size="md"
>
Request Access
</ReactButton>
}
/>
</div>
<div className="mbe4">
<h2>Compact Size Showcase</h2>
<p className="mbs2 mbe3">Small empty states work great in sidebars, cards, or dense layouts</p>
</div>
<div
style={{ display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(280px, 1fr))", gap: "var(--ag-space-4)" }}
className="mbe6"
>
<ReactEmptyState
title="No tasks"
subtitle="Create a task to get started"
buttonText="New Task"
size="sm"
isBordered={true}
isRounded={true}
className="empty-state-with-button"
/>
<ReactEmptyState
title="No events"
subtitle="Schedule your first event"
buttonText="Add Event"
size="sm"
isBordered={true}
isRounded={true}
className="empty-state-with-button"
/>
<ReactEmptyState
title="No contacts"
subtitle="Add contacts to your list"
buttonText="Add Contact"
size="sm"
isBordered={true}
isRounded={true}
className="empty-state-with-button"
/>
<ReactEmptyState
title="No notes"
subtitle="Jot down your first note"
buttonText="Create Note"
size="sm"
isBordered={true}
isRounded={true}
className="empty-state-with-button"
/>
</div>
<div className="mbe4">
<h2>CSS Shadow Parts Customization</h2>
<p className="mbs2 mbe3">
Use CSS Shadow Parts to customize appearance:
<code>::part(ag-empty-state)</code>,
<code>::part(ag-icon)</code>,
<code>::part(ag-title)</code>,
<code>::part(ag-subtitle)</code>,
<code>::part(ag-actions)</code>
</p>
</div>
<div className="mbe6">
{/* Colorful Accent */}
<ReactEmptyState
title="Colorful Accent"
subtitle="Icon with background circle"
buttonText="Start Now"
isBordered={true}
className="custom-empty-state-3"
icon={
<Rocket
size={48}
color="#f59e0b"
/>
}
/>
</div>
<div className="mbe4">
<h2>Minimal Empty States</h2>
<p className="mbs2 mbe3">Simple empty states without icons for clean, understated designs</p>
</div>
<div className="mbe6">
<ReactEmptyState
title="Nothing here yet"
size="sm"
isRounded={true}
className="mbe4"
/>
<ReactEmptyState
title="No activity"
subtitle="Recent activity will appear here"
size="md"
isRounded={true}
className="mbe4"
/>
<ReactEmptyState
title="Empty"
isRounded={true}
size="md"
/>
</div>
<div className="mbe4">
<h2>Large Welcome States</h2>
<p className="mbs2 mbe3">Spacious layouts perfect for onboarding and full-page empty states</p>
</div>
<ReactEmptyState
title="Welcome to Your Dashboard"
subtitle="Add some content and you'll see it appear here. You'll be able to manage projects and collaborate with your team."
buttonText="Get Started"
size="lg"
isBordered={true}
isRounded={true}
className="empty-state-with-button mbe6"
icon={
<Sparkles
size={48}
color="#6366f1"
/>
}
/>
<div className="mbe4">
<h2>Multiple Button Variants</h2>
<p className="mbs2 mbe3">Showcase different button styles in custom actions</p>
</div>
<ReactEmptyState
title="Choose your action"
subtitle="Different button styles for different purposes"
size="md"
isBordered={true}
isRounded={true}
className="mbe6"
icon={
<AlertCircle
size={40}
color="#999"
/>
}
actions={
<div style={{ display: "flex", gap: "0.5rem", flexWrap: "wrap", justifyContent: "center" }}>
<ReactButton
variant="primary"
size="sm"
shape="rounded"
>
Primary Action
</ReactButton>
<ReactButton
variant="secondary"
size="sm"
shape="rounded"
isBordered={true}
>
Secondary
</ReactButton>
<ReactButton
size="sm"
shape="rounded"
isBordered={true}
>
Default
</ReactButton>
</div>
}
/>
<div className="mbe4">
<h2>Button Size Variations</h2>
<p className="mbs2 mbe3">Match button sizes to your empty state size for visual harmony</p>
</div>
<ReactEmptyState
title="Different Button Sizes"
subtitle="Choose the right size for your layout"
size="md"
isBordered={true}
isRounded={true}
className="mbe6"
icon={
<Folder
size={48}
color="#999"
/>
}
actions={
<div style={{ display: "flex", gap: "0.5rem", alignItems: "center", justifyContent: "center", flexWrap: "wrap" }}>
<ReactButton
variant="primary"
size="sm"
shape="rounded"
isBordered={true}
>
<Mic size={14} />
<span style={{ marginInlineStart: "4px" }}>Small</span>
</ReactButton>
<ReactButton
variant="primary"
size="md"
shape="rounded"
isBordered={true}
>
<Mic size={18} />
<span style={{ marginInlineStart: "4px" }}>Medium</span>
</ReactButton>
<ReactButton
variant="primary"
size="lg"
shape="rounded"
isBordered={true}
>
<Mic size={24} />
<span style={{ marginInlineStart: "4px" }}>Large</span>
</ReactButton>
</div>
}
/>
</section>
);
};
export default EmptyStateExamples;
Usage β
TIP
The framework examples below import AgnosticUI as an npm package. Alternatively, you can use the CLI for complete control, AI/LLM visibility, and full code ownership:
npx ag init --framework FRAMEWORK # react, vue, lit, svelte, etc.
npx ag add EmptyStateThe CLI copies source code directly into your project, giving you full visibility and control. After running npx ag add, you'll receive exact import instructions.
Vue
<template>
<section>
<VueEmptyState
title="No items found"
subtitle="Get started by creating your first item"
/>
<VueEmptyState
title="No projects yet"
subtitle="Create your first project to get started"
buttonText="New Project"
/>
<VueEmptyState
title="No notifications"
subtitle="You're all caught up!"
size="sm"
/>
<VueEmptyState
title="Welcome to Your Dashboard"
subtitle="Start by adding some content"
buttonText="Get Started"
size="lg"
/>
<VueEmptyState
title="No messages"
subtitle="Send your first message to start a conversation"
buttonText="New Message"
:bordered="true"
:rounded="true"
/>
<VueEmptyState
title="No files"
subtitle="Drag and drop files here to upload"
:bordered="true"
>
<template #icon>
<VueIcon size="56" :noFill="true">
<svg
xmlns="http://www.w3.org/2000/svg"
width="56"
height="56"
viewBox="0 0 24 24"
fill="#999"
>
<path
d="M3 6a2 2 0 012-2h4l2 2h7a2 2 0 012 2v10a2 2 0 01-2 2H5a2 2 0 01-2-2V6z"
></path>
</svg>
</VueIcon>
</template>
</VueEmptyState>
<VueEmptyState
title="No team members"
subtitle="Invite people to collaborate on this project"
>
<template #actions>
<div style="display: flex; gap: 0.5rem; justify-content: center;">
<VueButton variant="primary" size="sm" shape="rounded">
Invite Members
</VueButton>
<VueButton variant="secondary" size="sm" shape="rounded">
Learn More
</VueButton>
</div>
</template>
</VueEmptyState>
</section>
</template>
<script>
import { VueEmptyState } from "agnosticui-core/empty-state/vue";
import VueIcon from "agnosticui-core/icon/vue";
import VueButton from "agnosticui-core/button/vue";
export default {
components: {
VueEmptyState,
VueIcon,
VueButton,
},
};
</script>React
import { ReactEmptyState } from "agnosticui-core/react";
import { ReactIcon } from "agnosticui-core/react";
import { ReactButton } from "agnosticui-core/react";
export default function EmptyStateExample() {
return (
<section>
<ReactEmptyState
title="No items found"
subtitle="Get started by creating your first item"
/>
<ReactEmptyState
title="No projects yet"
subtitle="Create your first project to get started"
buttonText="New Project"
/>
<ReactEmptyState
title="No notifications"
subtitle="You're all caught up!"
size="sm"
/>
<ReactEmptyState
title="Welcome to Your Dashboard"
subtitle="Start by adding some content"
buttonText="Get Started"
size="lg"
/>
<ReactEmptyState
title="No messages"
subtitle="Send your first message to start a conversation"
buttonText="New Message"
bordered
rounded
/>
<ReactEmptyState
title="No files"
subtitle="Drag and drop files here to upload"
bordered
>
<ReactIcon size="56" noFill slot="icon">
<svg
xmlns="http://www.w3.org/2000/svg"
width="56"
height="56"
viewBox="0 0 24 24"
fill="#999"
>
<path d="M3 6a2 2 0 012-2h4l2 2h7a2 2 0 012 2v10a2 2 0 01-2 2H5a2 2 0 01-2-2V6z"></path>
</svg>
</ReactIcon>
</ReactEmptyState>
<ReactEmptyState
title="No team members"
subtitle="Invite people to collaborate on this project"
>
<div
slot="actions"
style={{ display: "flex", gap: "0.5rem", justifyContent: "center" }}
>
<ReactButton variant="primary" size="sm" shape="rounded">
Invite Members
</ReactButton>
<ReactButton variant="secondary" size="sm" shape="rounded">
Learn More
</ReactButton>
</div>
</ReactEmptyState>
</section>
);
}Lit (Web Components)
<script type="module">
import "agnosticui-core/empty-state";
import "agnosticui-core/icon";
import "agnosticui-core/button";
</script>
<section>
<ag-empty-state
title="No items found"
subtitle="Get started by creating your first item"
></ag-empty-state>
<ag-empty-state
title="No projects yet"
subtitle="Create your first project to get started"
button-text="New Project"
></ag-empty-state>
<ag-empty-state
title="No notifications"
subtitle="You're all caught up!"
size="sm"
></ag-empty-state>
<ag-empty-state
title="Welcome to Your Dashboard"
subtitle="Start by adding some content"
button-text="Get Started"
size="lg"
></ag-empty-state>
<ag-empty-state
title="No messages"
subtitle="Send your first message to start a conversation"
button-text="New Message"
bordered
rounded
></ag-empty-state>
<ag-empty-state
title="No files"
subtitle="Drag and drop files here to upload"
bordered
>
<ag-icon slot="icon" size="56" no-fill>
<svg
xmlns="http://www.w3.org/2000/svg"
width="56"
height="56"
viewBox="0 0 24 24"
fill="#999"
>
<path
d="M3 6a2 2 0 012-2h4l2 2h7a2 2 0 012 2v10a2 2 0 01-2 2H5a2 2 0 01-2-2V6z"
></path>
</svg>
</ag-icon>
</ag-empty-state>
<ag-empty-state
title="No team members"
subtitle="Invite people to collaborate on this project"
>
<div
slot="actions"
style="display: flex; gap: 0.5rem; justify-content: center;"
>
<ag-button variant="primary" size="sm" shape="rounded">
Invite Members
</ag-button>
<ag-button variant="secondary" size="sm" shape="rounded">
Learn More
</ag-button>
</div>
</ag-empty-state>
</section>Props β
| Prop | Type | Default | Description |
|---|---|---|---|
title | string | '' | Primary heading text for the empty state. Should be concise and descriptive. |
subtitle | string | '' | Supporting text that provides additional context or guidance. |
buttonText | string | '' | Text for the default action button. If provided, a button will be rendered. |
size | 'sm' | 'md' | 'lg' | 'md' | Size variant that controls spacing and text sizing. |
bordered | boolean | false | Adds a border around the empty state for visual separation. |
rounded | boolean | false | Applies rounded corners to the empty state container. |
Slots β
| Slot | Description |
|---|---|
icon | Custom icon or illustration to display above the title. Typically used with the Icon component. |
actions | Custom action buttons or interactive elements. Overrides the default buttonText button. |
Size Variants β
The EmptyState component supports three size variants:
- Small (
sm): Compact layout with smaller text and spacing. Ideal for sidebars, cards, or inline empty states. - Medium (
md): Default size with balanced spacing. Suitable for most use cases. - Large (
lg): Spacious layout with larger text. Perfect for full-page empty states or prominent sections.
<VueEmptyState
title="No notifications"
subtitle="You're all caught up!"
size="sm"
/>
<VueEmptyState
title="No projects yet"
subtitle="Create your first project"
buttonText="New Project"
size="md"
/>
<VueEmptyState
title="Welcome!"
subtitle="Start your journey here"
buttonText="Get Started"
size="lg"
/>Visual Styling β
Bordered β
Add a border for better visual separation from surrounding content:
<VueEmptyState
title="No data available"
subtitle="Upload a file to see your data"
buttonText="Upload File"
:bordered="true"
/>Rounded β
Apply rounded corners for a softer, more modern appearance:
<VueEmptyState
title="Empty folder"
subtitle="This folder contains no files yet"
buttonText="Add Files"
:rounded="true"
/>Combined β
Use both bordered and rounded for a card-like appearance:
<VueEmptyState
title="No messages"
subtitle="Send your first message"
buttonText="New Message"
:bordered="true"
:rounded="true"
/>Custom Icons β
Use the icon slot to provide custom illustrations or icons that match your empty state context:
<VueEmptyState
title="No search results"
subtitle="Try different keywords or remove filters"
>
<template #icon>
<VueIcon size="40" :noFill="true">
<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="m13.5 8.5-5 5"/>
<path d="m8.5 8.5 5 5"/>
<circle cx="11" cy="11" r="8"/>
<path d="m21 21-4.3-4.3"/>
</svg>
</VueIcon>
</template>
</VueEmptyState>Icon Guidelines:
- Use simple, recognizable icons that clearly represent the empty state
- Keep icons appropriately sized (40-56px for medium/large empty states)
- Use neutral colors like gray unless you need to convey state (success, warning, error)
- Consider using SVG for crisp rendering at any size
Custom Actions β
The actions slot allows complete control over interactive elements:
<VueEmptyState
title="Start your journey"
subtitle="Choose how you want to begin"
>
<template #actions>
<div style="display: flex; gap: 0.5rem; justify-content: center;">
<VueButton variant="primary" size="md" shape="rounded">
Create New
</VueButton>
<VueButton variant="primary" :isBordered="true" size="md" shape="rounded">
Import Data
</VueButton>
<VueButton variant="primary" :isBordered="true" size="md" shape="rounded">
Use Template
</VueButton>
</div>
</template>
</VueEmptyState>Actions Best Practices:
- Prioritize actions - put the primary action first and make it most prominent
- Limit actions to 1-3 options to avoid overwhelming users
- Use clear, action-oriented labels ("Create Project" not just "Create")
- Consider button variants to establish visual hierarchy
Use Cases β
No Search Results β
<VueEmptyState
title="No search results"
subtitle="Try different keywords or remove filters"
size="md"
>
<template #icon>
<VueIcon size="40" :noFill="true" v-html="searchIcon" />
</template>
</VueEmptyState>Shopping Cart β
<VueEmptyState
title="Your cart is empty"
subtitle="Add items to your cart to see them here"
:bordered="true"
:rounded="true"
>
<template #icon>
<VueIcon size="48" :noFill="true" v-html="cartIcon" />
</template>
<template #actions>
<VueButton variant="primary" shape="rounded">
Continue Shopping
</VueButton>
</template>
</VueEmptyState>Inbox Zero β
<VueEmptyState
title="Inbox Zero! π"
subtitle="All caught up. No new messages."
size="md"
>
<template #icon>
<VueIcon size="40" type="info" v-html="inboxIcon" />
</template>
</VueEmptyState>Error State β
<VueEmptyState
title="Something went wrong"
subtitle="Please try again or contact support"
:bordered="true"
>
<template #icon>
<VueIcon size="48" type="error" v-html="alertIcon" />
</template>
<template #actions>
<div style="display: flex; gap: 0.5rem; justify-content: center;">
<VueButton variant="danger" shape="rounded">
Try Again
</VueButton>
<VueButton variant="secondary" shape="rounded">
Contact Support
</VueButton>
</div>
</template>
</VueEmptyState>Onboarding / Welcome β
<VueEmptyState
title="Welcome to Your Dashboard"
subtitle="Start by adding some content to see it appear here"
buttonText="Get Started"
size="lg"
:bordered="true"
:rounded="true"
/>Accessibility β
The EmptyState component follows accessibility best practices:
- Semantic Structure: Uses appropriate heading hierarchy for title and subtitle
- Color Contrast: Default text colors meet WCAG AA contrast requirements
- Keyboard Navigation: All interactive elements (buttons) are keyboard accessible
- Focus Management: Buttons receive proper focus indicators
- Screen Readers: Content is announced in logical order (icon β title β subtitle β actions)
Writing Accessible Content β
Titles should be:
- Clear and concise (3-5 words ideal)
- Descriptive of the empty state
- Written in sentence case
Subtitles should be:
- Provide helpful context or guidance
- Suggest next steps when appropriate
- Keep to 1-2 sentences
Button text should be:
- Action-oriented ("Create Project" not "Click Here")
- Specific about what will happen
- Brief but descriptive
<VueEmptyState
title="No projects yet"
subtitle="Create your first project to get started"
buttonText="Create Project"
/>
<VueEmptyState
title="Nothing here"
subtitle="There's nothing to show"
buttonText="Click here"
/>CSS Shadow Parts β
The EmptyState component exposes CSS Shadow Parts for custom styling:
| Part | Description |
|---|---|
ag-empty-state | The main container element |
icon | The icon slot wrapper |
title | The title/heading element |
subtitle | The subtitle/description element |
actions | The actions slot wrapper |
actions-button | The default button (when using buttonText prop) |
Customization Examples β
ag-empty-state::part(ag-empty-state) {
border: 2px solid transparent;
background: linear-gradient(white, white) padding-box, linear-gradient(
135deg,
#667eea 0%,
#764ba2 100%
) border-box;
border-radius: 16px;
padding: 2rem;
}
ag-empty-state::part(ag-title) {
color: #667eea;
font-weight: 700;
}
ag-empty-state::part(ag-empty-state) {
background: linear-gradient(135deg, #1e293b 0%, #334155 100%);
border: 2px solid #475569;
border-radius: 12px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
}
ag-empty-state::part(ag-icon) {
color: #60a5fa;
filter: drop-shadow(0 0 8px rgba(96, 165, 250, 0.5));
}
ag-empty-state::part(ag-title) {
color: #f1f5f9;
font-weight: 700;
}
ag-empty-state::part(ag-subtitle) {
color: #cbd5e1;
}
ag-empty-state::part(ag-icon) {
color: #f59e0b;
background: rgba(245, 158, 11, 0.1);
border-radius: 50%;
padding: 1rem;
}
ag-empty-state::part(ag-actions-button) {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border: none;
border-radius: 8px;
padding: 0.75rem 1.5rem;
font-weight: 600;
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4);
transition: all 0.2s;
}
ag-empty-state::part(ag-actions-button):hover {
transform: translateY(-2px);
box-shadow: 0 6px 16px rgba(102, 126, 234, 0.5);
}Best Practices β
Do's β
β Be empathetic and helpful - Acknowledge the empty state and guide users toward action
<VueEmptyState
title="No tasks yet"
subtitle="Create your first task to get organized"
buttonText="Create Task"
/>β Match the tone to context - Celebratory for achievements, helpful for empty lists
<VueEmptyState
title="Inbox Zero! π"
subtitle="Great job! You've cleared all your messages"
/>
<VueEmptyState
title="No contacts yet"
subtitle="Add contacts to start connecting"
buttonText="Add Contact"
/>β Provide clear next steps - Always suggest what users can do
<VueEmptyState
title="No analytics data"
subtitle="Data will appear once you start tracking events"
buttonText="View Documentation"
/>β Use appropriate icons - Choose icons that clearly represent the context
<template #icon>
<VueIcon size="40" v-html="searchIcon" />
</template>Don'ts β
β Don't be vague or unhelpful
<VueEmptyState title="Nothing here" subtitle="There's nothing to show" />β Don't overwhelm with too many actions
<template #actions>
<VueButton>Action 1</VueButton>
<VueButton>Action 2</VueButton>
<VueButton>Action 3</VueButton>
<VueButton>Action 4</VueButton>
<VueButton>Action 5</VueButton>
</template>β Don't use negative or discouraging language
<VueEmptyState
title="Oops! Nothing found"
subtitle="You haven't done anything yet"
/>β Don't forget the icon for prominent empty states
<VueEmptyState title="Welcome!" subtitle="Get started now" size="lg">
<template #icon>
<VueIcon size="56" v-html="welcomeIcon" />
</template>
</VueEmptyState>Common Patterns β
Filtered List with No Results β
<VueEmptyState
title="No matches found"
subtitle="Try adjusting your filters or search terms"
>
<template #actions>
<VueButton variant="secondary" @click="clearFilters">
Clear Filters
</VueButton>
</template>
</VueEmptyState>Loading State Alternative β
While not a loading indicator, empty states can acknowledge that content is coming:
<VueEmptyState
title="Getting things ready"
subtitle="Your content will appear here once it's loaded"
size="sm"
/>Permission/Access Denied β
<VueEmptyState
title="Access Required"
subtitle="You don't have permission to view this content"
:bordered="true"
>
<template #icon>
<VueIcon size="48" type="warning" v-html="lockIcon" />
</template>
<template #actions>
<VueButton variant="primary">
Request Access
</VueButton>
</template>
</VueEmptyState>Integration with Data Loading β
Empty states work well within conditional rendering based on data state:
<template>
<div>
<div v-if="loading">
<LoadingSpinner />
</div>
<div v-else-if="error">
<VueEmptyState
title="Failed to load data"
subtitle="Please try again or contact support"
>
<template #actions>
<VueButton variant="primary" @click="retry"> Try Again </VueButton>
</template>
</VueEmptyState>
</div>
<div v-else-if="items.length === 0">
<VueEmptyState
title="No items yet"
subtitle="Create your first item to get started"
buttonText="Create Item"
/>
</div>
<div v-else>
<ItemList :items="items" />
</div>
</div>
</template>