Skip to content

Empty

It fills the place content would have taken, with a glyph, a headline, a sentence and a way out. It is for the list that came back with nothing, the inbox nobody has written to, and the folder before its first file.

tsx
import { Button, Empty } from 'neba';

<Empty title="No projects yet" action={<Button size="sm">Create a project</Button>}>
  Everything you deploy shows up here.
</Empty>;

Props

PropTypeDefaultDescription
variantshared'solid' | 'outline' | 'text''text'Weight of the surface. text is the default here and nowhere else: an empty state is nearly always already inside something(a Card body, a Table) and a second rectangle drawn inside the first is one rectangle too many
sizeshared'xs' | 'sm' | 'md' | 'lg' | 'xl''md'The type scale, the glyph, and how much room the state takes vertically
colorshared'primary' | 'secondary' | 'success' | 'warning' | 'danger' | 'info''secondary'Colour family. The sheet is never dyed; it reaches the hairline and the focus ring and stops. Worth moving off secondary only when the emptiness is itself a problem: danger on a region that failed to load
densityshared'default' | 'compact''default'Padding only: never the height, never the type scale
elevationshared0 | 1 | 2 | 30Drop shadow depth. 0 is almost always right: an empty state is a hole in a surface that already exists rather than a sheet of its own
titleReactNode | falselocale's wordingThe headline. Defaults to the locale's way of saying that there is nothing here; false leaves the glyph and the sentence with no heading over them
iconReactNode | falseThe glyph above the headline. Defaults to the empty tray; false drops it. An svg is sized off the size ladder, and anything else(an illustration, a brand mark) is left at the size it came in at
actionReactNodeWhat to do about it, under the text: a "Create the first one" button, a "Clear filters" link. Several sit in a row and wrap together
localestring'en'Which language the default headline is written in: a BCP 47 tag. Ignored once title is given, and unsupported tags fall back to English
childrenReactNodeThe sentence under the headline: why it is empty, or what to do next
transitionsharedNebaTransitionAn entrance animation, run once on mount (transition="fade"). Wrap it in an Animate* component for a trigger or a replay
renderuseRender.RenderPropRenders something other than a div (render={<td colSpan={5} />}). Base UI's own escape hatch

Native <div> attributes pass through, and render swaps the element. The shared axes are described in prop conventions.

Examples

title

The headline defaults to the locale's way of saying that there is nothing here, so an Empty with no props at all still says something. Pass title for the version that names what is missing (which is nearly always worth doing), or title={false} for a state that is a glyph and a sentence with no heading over them.

icon

icon takes any node and replaces the default tray: an icon from another set, an illustration, a brand mark. An <svg> is sized off the size ladder; anything else is left at the size it came in at. icon={false} drops it.

action

action sits under the text and is where the state stops being a dead end: a button that creates the first record, a link that clears the filter that matched nothing. Pass a fragment for more than one and they lay out in a row that wraps together.

variant

text is the default, and it is the default here and nowhere else: an empty state is usually already inside a Card, a Table or a panel, and a second rectangle inside the first is one rectangle too many. outline and solid are for the case where nothing else marks the bounds of the region.

size and density

size sets the type scale, the glyph and how much room the state takes vertically. density changes the padding and nothing else, which is what to reach for when the state has to fit inside a control-sized row.

color

The sheet is never dyed: color reaches the hairline and the focus ring and stops there. secondary is the default because an empty state arriving in the accent colour is making a claim about content that does not exist. Move it when the emptiness is itself a problem.

locale

locale is a BCP 47 tag and decides the default headline only. It is ignored once title is given, and an unsupported tag falls back to English.

Inside a Table

Table takes an empty prop for what to show instead of rows, and it renders into a cell spanning every column. That cell has padding of its own, so density="compact" is usually the right pairing.

Accessibility

  • The root is a role="status" live region, so a list that empties under the reader announces itself rather than going silently blank. Pass role={undefined} for a state that is simply part of the page when it arrives.
  • The default glyph is aria-hidden. It names nothing the headline does not already say.
  • Reach for Skeleton while content is still on its way and Empty only once it is known that none is coming. Showing neither leaves a blank rectangle where the answer should be.

Released under the MIT License