Empty state
What a list, a table or a search shows when there is nothing to show: an icon, a line, a sentence and your own action.
Empty state
server - 0 KBWhat a list, a table or a search shows when there is nothing to show: an icon, a line, a sentence and your own action.
Nothing here yet
The code
import { EmptyState } from 'topclassui/empty-state'
/** No props at all: the tray icon, then the English title and description from labels. */
export default function Demo() {
return (
<div className="max-w-85">
<EmptyState />
</div>
)
}
With no props it shows the tray icon and the two labels.state strings; a title of your own drops the default description.
Every state
5 statesNothing here yet
Title only
With action
Failed
Filtered
Import from the package
import { EmptyState } from 'topclassui/empty-state'Install once:npm i topclassui@beta zod
Examples
- Pass a
titleand the default description goes too, because it would describe the wrong thing. nullhides theicon, thetitleor thedescription.- The action is your own Button, passed as
children; the component draws none of its own. - A search with no match or a list that failed to load is another
iconandtitle, not a variant.
Nothing here yet
import { EmptyState } from 'topclassui/empty-state'
/** No props at all: the tray icon, then the English title and description from labels. */
export default function Demo() {
return (
<div className="max-w-85">
<EmptyState />
</div>
)
}
import { plus } from 'topclassui/icons/plus'
import { Svg } from 'topclassui/svg'
import { Button } from 'topclassui/button'
import { EmptyState } from 'topclassui/empty-state'
/** The drawing: a caller's title and description, and a link styled as a small Button. */
export default function Demo() {
return (
<div className="max-w-85">
<EmptyState
title="No projects yet"
description="Create your first project and it will show up here."
>
<Button size="sm" render={<a href="/projects/new" />}>
<Svg icon={plus} />
New project
</Button>
</EmptyState>
</div>
)
}
No matches
import { magnifyingGlass } from 'topclassui/icons/magnifying-glass'
import { Svg } from 'topclassui/svg'
import { Button } from 'topclassui/button'
import { EmptyState } from 'topclassui/empty-state'
/** The filtered cell: another icon and title, not a variant, and a Clear filters button. */
export default function Demo() {
return (
<div className="max-w-85">
<EmptyState
icon={<Svg icon={magnifyingGlass} size="xl" className="text-muted" />}
title="No matches"
description="Nothing matches that search. Try fewer words, or clear the filters."
>
<Button size="sm" variant="outline">Clear filters</Button>
</EmptyState>
</div>
)
}
The invoices did not load
import { warningCircle } from 'topclassui/icons/warning-circle'
import { Svg } from 'topclassui/svg'
import { Button } from 'topclassui/button'
import { EmptyState } from 'topclassui/empty-state'
/** The error cell: the warning icon in the danger colour, and a Try again button. */
export default function Demo() {
return (
<div className="max-w-85">
<EmptyState
icon={<Svg icon={warningCircle} size="xl" className="text-danger-on-soft" />}
title="The invoices did not load"
description="Check your connection, then try again."
>
<Button size="sm" variant="outline">Try again</Button>
</EmptyState>
</div>
)
}
No comments yet
import { EmptyState } from 'topclassui/empty-state'
/** icon={null} hides the icon, and a caller's title with no description shows no description. */
export default function Demo() {
return (
<div className="max-w-85">
<EmptyState icon={null} title="No comments yet" />
</div>
)
}
Nothing in https://example.com/workspaces/finance/reports/2026/quarterly/archive/unfiled
import { EmptyState } from 'topclassui/empty-state'
const PATH = 'https://example.com/workspaces/finance/reports/2026/quarterly/archive/unfiled'
/** A 200 px column: the URL has no space to break at, so wrap-anywhere breaks it inside the box. */
export default function Demo() {
return (
<div className="w-50">
<EmptyState
title={`Nothing in ${PATH}`}
description="Files you move into this folder will show up here, newest first."
/>
</div>
)
}
import { plus } from 'topclassui/icons/plus'
import { ar } from 'topclassui/labels/ar'
import { Svg } from 'topclassui/svg'
import { Button } from 'topclassui/button'
import { EmptyState } from 'topclassui/empty-state'
/** The Arabic labels give the default title and description; the action is the caller's. */
export default function Demo() {
return (
<div dir="rtl" lang="ar" className="max-w-85">
<EmptyState labels={ar}>
<Button size="sm" render={<a href="/projects/new" />}>
<Svg icon={plus} />
مشروع جديد
</Button>
</EmptyState>
</div>
)
}
Props
Keyboard and accessibility
Nothing to operate: it is not interactive on its own.
- It has no role and no live region: it is page content, read in place.
- The title is a paragraph, not a heading, so it adds nothing to the page outline.
- The default icon is decorative and takes the muted colour; an icon you pass keeps its own.
Right to left
A centred column, so nothing mirrors. With the Arabic labels the default title and description are Arabic.
Server and client
Renders on the server and ships no JavaScript.
Files: index.tsx.
On the chip: server - 0 KB.