Empty
A placeholder for screens with nothing to show yet, with an icon, a message and the next action.
pnpm dlx shadcn@latest add https://hextaui.com/r/empty.jsonAdds the component, the HextaUI theme tokens and any HextaUI components it depends on.
Add the theme tokens to your global CSS, if you haven’t yet.
Install the dependencies.
pnpm add @base-ui/react class-variance-authority cnCopy and paste the following code into your project.
components/ui/empty.tsx Update the import paths to match your project setup.
Entrance
When an empty state replaces content, its parts fade up one after another: media, title, description, then actions. Dismiss the notifications to see it. Under reduced motion it just appears. Set animated={false} for empty states that are there from the first paint.
- Jun approved your pull request
- Mira mentioned you in #design
- Deploy to production finished
Outline
variant="outline" draws a dashed hairline border, the usual way to mark a drop zone or an area waiting for content.
Muted
variant="muted" sits on a soft fill. The icon tile switches to a raised white surface so it still stands out.
Small
size="sm" tightens the padding, gaps, icon tile and title for sidebars, popovers and narrow panels.
Avatar
The default EmptyMedia variant only centers its content, so any avatar, badge or illustration fits.
Avatar group
Show who could be here, for invites and shared spaces.
Image
Give images explicit dimensions so the layout doesn’t shift while they load.
No search results
Swap the results for an empty state when filters match nothing, and offer a way out. A visually hidden role="status" announces the count, so screen reader users hear the change without the whole list being read.
1 task
- Plan the next sprint
In a card
Use size="sm" with the muted variant to fill a card that has no data yet.
In a table
Put it in a single cell that spans every column with colSpan, so the header stays aligned.
| Invoice | Customer | Amount |
|---|---|---|
No invoices Invoices you send will show up here. | ||
In a popover
A compact empty state for menus and notification panels.
In a command list
<CommandEmpty /> passes the current search to a function child, so the message can quote it.
Heading level
The title is a <div> so it never breaks your page outline. Render it as the heading level that fits, and label the region with it.
No drafts
Drafts are saved automatically while you write.
Long content
Unbroken names and long addresses wrap inside the container, and long button labels truncate.
We searched every folder shared with [email protected] and found nothing.
Check the sharing settings or ask the owner for access.
Right to left
Everything is centered and uses logical properties, so it reads naturally in right-to-left layouts.
<EmptyTitle />renders a<div>. Passrender={<h2 />}(or the level that fits) when the empty state is a section of the page.<EmptyMedia variant="icon" />is hidden from assistive tech because the title already says what it means. Images in the default variant stay exposed, so give them analtoralt="".- When results disappear after a search or filter, announce the change with a short
role="status"message next to the list. Don’t make the whole empty state a live region, since its buttons and links would be read every time.
Every part renders a plain element, accepts its attributes and supports the render prop to swap it.
| Prop | Type | Default |
|---|---|---|
variant | "default" | "outline" | "muted" | "default" |
sizesm tightens padding, gaps, the icon tile and the title. | "default" | "sm" | "default" |
animatedFade the parts up in sequence when the empty state mounts. Skipped under reduced motion. | boolean | true |
render | ReactElement | (props, state) => ReactElement | <div> |
| Attribute | Description |
|---|---|
data-slot="empty" | Target the root in CSS. |
data-variant | The current variant. |
data-size | The current size. |
data-animated | Present when the entrance animation is on. |
--empty-padding | Padding on every side. Set by size. |
--empty-gap | Space between the header and content. Set by size. |
| Prop | Type | Default |
|---|---|---|
render | ReactElement | (props, state) => ReactElement | <div> |
| Attribute | Description |
|---|---|
data-slot="empty-header" | Target the header in CSS. |
| Prop | Type | Default |
|---|---|---|
varianticon puts an icon in a muted tile. stack raises the tile over two fanned cards that spread when the empty state is hovered. Both hide the media from assistive tech. | "default" | "icon" | "stack" | "default" |
render | ReactElement | (props, state) => ReactElement | <div> |
| Attribute | Description |
|---|---|
data-slot="empty-icon" | Target the media in CSS. Matches shadcn. |
data-variant | The current variant. |
| Prop | Type | Default |
|---|---|---|
renderRender a heading, such as render={<h2 />}. | ReactElement | (props, state) => ReactElement | <div> |
| Attribute | Description |
|---|---|
data-slot="empty-title" | Target the title in CSS. |
| Prop | Type | Default |
|---|---|---|
renderPlain links inside are underlined. Components rendered as links keep their own style. | ReactElement | (props, state) => ReactElement | <div> |
| Attribute | Description |
|---|---|
data-slot="empty-description" | Target the description in CSS. |
| Prop | Type | Default |
|---|---|---|
render | ReactElement | (props, state) => ReactElement | <div> |
| Attribute | Description |
|---|---|
data-slot="empty-content" | Target the actions area in CSS. |