Skip to content

Empty State ​

Experimental Alpha

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 ​

Vue
Lit
React
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

Invite Members Learn More
Create NewImport DataUse Template

Real-World Examples

Common patterns for shopping cart, inbox, dashboard, and error states

Continue Shopping View Documentation
Try Again Contact Support

State-Specific Variants

Empty states for different scenarios with appropriate icons and actions

Clear Filters
Proceed Anyway Cancel
Request Access

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

Primary Action Secondary Default

Button Size Variations

Match button sizes to your empty state size for visual harmony

SmallMediumLarge
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;
Open in StackBlitz

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:

bash
npx ag init --framework FRAMEWORK # react, vue, lit, svelte, etc.
npx ag add EmptyState

The 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
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
tsx
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)
html
<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 ​

PropTypeDefaultDescription
titlestring''Primary heading text for the empty state. Should be concise and descriptive.
subtitlestring''Supporting text that provides additional context or guidance.
buttonTextstring''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.
borderedbooleanfalseAdds a border around the empty state for visual separation.
roundedbooleanfalseApplies rounded corners to the empty state container.

Slots ​

SlotDescription
iconCustom icon or illustration to display above the title. Typically used with the Icon component.
actionsCustom 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.
vue
<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:

vue
<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:

vue
<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:

vue
<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:

vue
<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:

vue
<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 ​

vue
<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 ​

vue
<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 ​

vue
<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 ​

vue
<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 ​

vue
<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
vue
<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:

PartDescription
ag-empty-stateThe main container element
iconThe icon slot wrapper
titleThe title/heading element
subtitleThe subtitle/description element
actionsThe actions slot wrapper
actions-buttonThe default button (when using buttonText prop)

Customization Examples ​

css
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

vue
<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

vue
<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

vue
<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

vue
<template #icon>
  <VueIcon size="40" v-html="searchIcon" />
</template>

Don'ts ​

❌ Don't be vague or unhelpful

vue
<VueEmptyState title="Nothing here" subtitle="There's nothing to show" />

❌ Don't overwhelm with too many actions

vue
<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

vue
<VueEmptyState
  title="Oops! Nothing found"
  subtitle="You haven't done anything yet"
/>

❌ Don't forget the icon for prominent empty states

vue
<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 ​

vue
<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:

vue
<VueEmptyState
  title="Getting things ready"
  subtitle="Your content will appear here once it's loaded"
  size="sm"
/>

Permission/Access Denied ​

vue
<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:

vue
<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>

Resources ​