Skip to content
Navigation

Empty State

Centered placeholder for a region with nothing in it, with room for the action that fills it. Upstream calls this component `empty`.

Added 14 October 2026

React

Codecomponents/react/empty-state-demo.tsx
import {
    Button,
    EmptyState,
    EmptyStateContent,
    EmptyStateDescription,
    EmptyStateHeader,
    EmptyStateMedia,
    EmptyStateTitle,
} from '@stroeer-labs/design-library/react';
import {FolderPlusIcon, MegaphoneIcon, SearchXIcon} from 'lucide-react';

export function EmptyStateDemo() {
    return (
        <div className="space-y-6">
            <div className="space-y-2">
                <p className="text-xs font-medium text-muted-foreground">Nothing created yet — offer the first action</p>
                <EmptyState className="border">
                    <EmptyStateHeader>
                        <EmptyStateMedia variant="icon">
                            <FolderPlusIcon />
                        </EmptyStateMedia>
                        <EmptyStateTitle>No campaigns yet</EmptyStateTitle>
                        <EmptyStateDescription>Create your first campaign to start booking inventory.</EmptyStateDescription>
                    </EmptyStateHeader>
                    <EmptyStateContent className="flex-row justify-center gap-2">
                        <Button>Create campaign</Button>
                        <Button variant="outline">Import</Button>
                    </EmptyStateContent>
                </EmptyState>
            </div>
            <div className="space-y-2">
                <p className="text-xs font-medium text-muted-foreground">Nothing matched — announced, because it replaced content</p>
                {/* role="status" only when the empty state REPLACES content in place; an empty state that is
                    the initial page content must not be a live region. See docs/components/empty-state.md. */}
                <EmptyState role="status" className="border">
                    <EmptyStateHeader>
                        <EmptyStateMedia variant="icon">
                            <SearchXIcon />
                        </EmptyStateMedia>
                        <EmptyStateTitle>No sites match these filters</EmptyStateTitle>
                        <EmptyStateDescription>Try widening the venue type or removing the date range.</EmptyStateDescription>
                    </EmptyStateHeader>
                    <EmptyStateContent>
                        <Button variant="outline">Clear all filters</Button>
                    </EmptyStateContent>
                </EmptyState>
            </div>
            <div className="space-y-2">
                <p className="text-xs font-medium text-muted-foreground">
                    Section-level, icon-lg media — borderless on purpose; the accent tint sits on the icon, never the well
                </p>
                <EmptyState>
                    <EmptyStateHeader>
                        <EmptyStateMedia variant="icon-lg" className="text-brand-interactive">
                            <MegaphoneIcon />
                        </EmptyStateMedia>
                        <EmptyStateTitle>No active campaigns</EmptyStateTitle>
                        <EmptyStateDescription>Book your first campaign and it will show up here.</EmptyStateDescription>
                    </EmptyStateHeader>
                    <EmptyStateContent>
                        <Button>Book a campaign</Button>
                    </EmptyStateContent>
                </EmptyState>
            </div>
            <div className="space-y-2">
                <p className="text-xs font-medium text-muted-foreground">Without media, no border — inline inside a panel</p>
                <EmptyState>
                    <EmptyStateHeader>
                        <EmptyStateTitle>No line items</EmptyStateTitle>
                        <EmptyStateDescription>Add a line item to allocate budget.</EmptyStateDescription>
                    </EmptyStateHeader>
                </EmptyState>
            </div>
        </div>
    );
}

Angular

Codecomponents/angular/empty-state-demo.ts
import {Component} from '@angular/core';
import {HlmButtonImports, HlmEmptyStateImports} from '@stroeer-labs/design-library/angular';
import {NgIcon, provideIcons} from '@ng-icons/core';
import {lucideFolderPlus, lucideMegaphone, lucideSearchX} from '@ng-icons/lucide';

@Component({
    selector: 'dl-empty-state-demo',
    standalone: true,
    imports: [HlmEmptyStateImports, HlmButtonImports, NgIcon],
    providers: [provideIcons({lucideFolderPlus, lucideMegaphone, lucideSearchX})],
    template: `
        <div class="space-y-6">
            <div class="space-y-2">
                <p class="text-xs font-medium text-muted-foreground">Nothing created yet — offer the first action</p>
                <hlm-empty-state class="border">
                    <hlm-empty-state-header>
                        <hlm-empty-state-media variant="icon">
                            <ng-icon name="lucideFolderPlus" />
                        </hlm-empty-state-media>
                        <div hlmEmptyStateTitle>No campaigns yet</div>
                        <div hlmEmptyStateDescription>Create your first campaign to start booking inventory.</div>
                    </hlm-empty-state-header>
                    <hlm-empty-state-content class="flex-row justify-center gap-2">
                        <button hlmBtn>Create campaign</button>
                        <button hlmBtn variant="outline">Import</button>
                    </hlm-empty-state-content>
                </hlm-empty-state>
            </div>
            <div class="space-y-2">
                <p class="text-xs font-medium text-muted-foreground">Nothing matched — announced, because it replaced content</p>
                <!-- role="status" only when the empty state REPLACES content in place; an empty state that is
                     the initial page content must not be a live region. See docs/components/empty-state.md. -->
                <hlm-empty-state role="status" class="border">
                    <hlm-empty-state-header>
                        <hlm-empty-state-media variant="icon">
                            <ng-icon name="lucideSearchX" />
                        </hlm-empty-state-media>
                        <div hlmEmptyStateTitle>No sites match these filters</div>
                        <div hlmEmptyStateDescription>Try widening the venue type or removing the date range.</div>
                    </hlm-empty-state-header>
                    <hlm-empty-state-content>
                        <button hlmBtn variant="outline">Clear all filters</button>
                    </hlm-empty-state-content>
                </hlm-empty-state>
            </div>
            <div class="space-y-2">
                <p class="text-xs font-medium text-muted-foreground">
                    Section-level, icon-lg media — borderless on purpose; the accent tint sits on the icon, never the well
                </p>
                <hlm-empty-state>
                    <hlm-empty-state-header>
                        <hlm-empty-state-media variant="icon-lg" class="text-brand-interactive">
                            <ng-icon name="lucideMegaphone" />
                        </hlm-empty-state-media>
                        <h2 hlmEmptyStateTitle>No active campaigns</h2>
                        <div hlmEmptyStateDescription>Book your first campaign and it will show up here.</div>
                    </hlm-empty-state-header>
                    <hlm-empty-state-content>
                        <button hlmBtn>Book a campaign</button>
                    </hlm-empty-state-content>
                </hlm-empty-state>
            </div>
            <div class="space-y-2">
                <p class="text-xs font-medium text-muted-foreground">Without media, no border — inline inside a panel</p>
                <hlm-empty-state>
                    <hlm-empty-state-header>
                        <div hlmEmptyStateTitle>No line items</div>
                        <div hlmEmptyStateDescription>Add a line item to allocate budget.</div>
                    </hlm-empty-state-header>
                </hlm-empty-state>
            </div>
        </div>
    `,
})
export class EmptyStateDemoComponent {}