Components · Feedback
Empty state
Nothing here yet, or no results: a mark, what is going on, what to do, announced when it replaces results.
0.78% of the 2,062 sites in The UI Index have a empty state (16 sites).
No projects yet
Connect a repository and your first deploy starts right away.
No deploys match “pricng”
Check the spelling, or clear the filters.
No projects yet
Connect a repository and your first deploy starts right away.
No deploys match “pricng”
Check the spelling, or clear the filters.
Both previews render the same markup; the HTML tab below is that markup without a framework.
Install
- React
- npx shadcn@
latest add https:// www.themes.dev/ caseroom/ r/ empty-state.json - Astro
- npx shadcn@
latest add https:// www.themes.dev/ caseroom/ r/ astro/ empty-state.json
Or copy the files below into your project. They need clsx, tailwind-merge, and the Caseroom theme. The HTML needs only the compiled CSS.
import type { ReactNode } from 'react';
import { cn } from '@/lib/utils';
import { emptyState, MARKS } from './empty-state.variants';
export interface EmptyStateProps {
title: string;
description?: string;
mark?: keyof typeof MARKS;
/** It replaces results (a search, a filter): announce it. */
live?: boolean;
/** One or two actions. */
children?: ReactNode;
className?: string;
}
/** Nothing here yet, or no results: a mark, what is going on, what to do. */
export function EmptyState({ title, description, mark = 'inbox', live, children, className }: EmptyStateProps) {
return (
<div role={live ? 'status' : undefined} className={cn(emptyState.root, className)}>
<span className={emptyState.mark} aria-hidden="true"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"><path d={MARKS[mark]} /></svg></span>
<p className={emptyState.title}>{title}</p>
{description && <p className={emptyState.description}>{description}</p>}
{children && <div className={emptyState.actions}>{children}</div>}
</div>
);
}/** An empty state: nothing here yet, or no results. A small drawn mark, one line on what is going on, one on what to
* do, and at most two actions. Centred on its own (it is the whole content of its area); role=status when it
* replaces results, so the change is announced. */
export const emptyState = {
// h-full: side by side, the actions of a row line up at the bottom.
root: 'mx-auto flex h-full w-full max-w-sm flex-col items-center px-6 py-10 text-center',
mark: 'grid size-12 place-items-center rounded-full bg-muted text-muted-foreground [&_svg]:size-5',
title: 'mt-4 text-base font-semibold text-balance',
description: 'mt-1.5 text-sm text-pretty text-muted-foreground',
actions: 'mt-auto flex flex-wrap justify-center gap-2 pt-5',
};
export const MARKS = {
search: 'M7 12.5a5.5 5.5 0 1 0 0-11 5.5 5.5 0 0 0 0 11zM11 11l3.5 3.5',
inbox: 'M2 9h3.5l1 2h3l1-2H14M2 9l1.5-5.5h9L14 9v3.5H2z',
plus: 'M8 3v10M3 8h10',
};---
import { cn } from '@/lib/utils';
import { emptyState, MARKS } from './empty-state.variants';
interface Props {
title: string;
description?: string;
mark?: keyof typeof MARKS;
/** It replaces results (a search, a filter): announce it. */
live?: boolean;
class?: string;
}
const { title, description, mark = 'inbox', live, class: className } = Astro.props;
---
<div role={live ? 'status' : undefined} class={cn(emptyState.root, className)}>
<span class={emptyState.mark} aria-hidden="true"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d={MARKS[mark]} /></svg></span>
<p class={emptyState.title}>{title}</p>
{description && <p class={emptyState.description}>{description}</p>}
{Astro.slots.has('default') && <div class={emptyState.actions}><slot /></div>}
</div>/** An empty state: nothing here yet, or no results. A small drawn mark, one line on what is going on, one on what to
* do, and at most two actions. Centred on its own (it is the whole content of its area); role=status when it
* replaces results, so the change is announced. */
export const emptyState = {
// h-full: side by side, the actions of a row line up at the bottom.
root: 'mx-auto flex h-full w-full max-w-sm flex-col items-center px-6 py-10 text-center',
mark: 'grid size-12 place-items-center rounded-full bg-muted text-muted-foreground [&_svg]:size-5',
title: 'mt-4 text-base font-semibold text-balance',
description: 'mt-1.5 text-sm text-pretty text-muted-foreground',
actions: 'mt-auto flex flex-wrap justify-center gap-2 pt-5',
};
export const MARKS = {
search: 'M7 12.5a5.5 5.5 0 1 0 0-11 5.5 5.5 0 0 0 0 11zM11 11l3.5 3.5',
inbox: 'M2 9h3.5l1 2h3l1-2H14M2 9l1.5-5.5h9L14 9v3.5H2z',
plus: 'M8 3v10M3 8h10',
};<div class="grid w-full max-w-2xl gap-6 sm:grid-cols-2">
<div class="mx-auto flex h-full w-full max-w-sm flex-col items-center px-6 py-10 text-center">
<span class="grid size-12 place-items-center rounded-full bg-muted text-muted-foreground [&_svg]:size-5" aria-hidden="true">
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M8 3v10M3 8h10"></path></svg>
</span>
<p class="mt-4 text-base font-semibold text-balance">No projects yet</p>
<p class="mt-1.5 text-sm text-pretty text-muted-foreground">Connect a repository and your first deploy starts right away.</p>
<div class="mt-auto flex flex-wrap justify-center gap-2 pt-5">
<button type="button" class="inline-flex shrink-0 cursor-pointer items-center justify-center gap-2 rounded-md font-medium whitespace-nowrap transition-[color,background-color,border-color,transform] select-none active:scale-[0.97] aria-busy:cursor-progress disabled:pointer-events-none disabled:opacity-50 aria-busy:disabled:opacity-80 aria-disabled:pointer-events-none aria-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 bg-primary text-primary-foreground hover:bg-[color-mix(in_oklab,var(--primary)_var(--hover-amount),var(--background))] active:bg-[color-mix(in_oklab,var(--primary)_var(--press-amount),var(--press-mix))] h-8 px-3 text-[13px]">
New project
</button>
</div>
</div>
<div role="status" class="mx-auto flex h-full w-full max-w-sm flex-col items-center px-6 py-10 text-center">
<span class="grid size-12 place-items-center rounded-full bg-muted text-muted-foreground [&_svg]:size-5" aria-hidden="true">
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M7 12.5a5.5 5.5 0 1 0 0-11 5.5 5.5 0 0 0 0 11zM11 11l3.5 3.5"></path></svg>
</span>
<p class="mt-4 text-base font-semibold text-balance">No deploys match “pricng”</p>
<p class="mt-1.5 text-sm text-pretty text-muted-foreground">Check the spelling, or clear the filters.</p>
<div class="mt-auto flex flex-wrap justify-center gap-2 pt-5">
<button type="button" class="inline-flex shrink-0 cursor-pointer items-center justify-center gap-2 rounded-md font-medium whitespace-nowrap transition-[color,background-color,border-color,transform] select-none active:scale-[0.97] aria-busy:cursor-progress disabled:pointer-events-none disabled:opacity-50 aria-busy:disabled:opacity-80 aria-disabled:pointer-events-none aria-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 border border-input bg-background hover:bg-accent hover:text-accent-foreground active:bg-pressed h-8 px-3 text-[13px]">
Clear filters
</button>
</div>
</div>
</div>