Data Display

Empty Panel

A composable empty state — a centred panel with a media slot, a real heading and paragraph, and an action area you compose yourself from buttons, links, or an input.

Usage

No invoices yet

Invoices you create or import will appear here. You can start from a template or bring in an existing ledger.

EmptyPanel is the placeholder a surface falls back to when it has nothing to show: an unfiltered list that is genuinely empty, a search that matched nothing, a folder before its first upload, a dashboard before its first event.

It is named EmptyPanel rather than EmptyState because the free @blakeui/react already exports an EmptyState and owns the .empty-state class. That one is a bare root — a single element with muted text and no parts. This is the composable Pro version, and the two are independent: importing this one changes nothing about the other.

The API is composable rather than a single component with props, which is the same exception the Legend and the Timeline take. The action area carries a button pair, a link, or a search input, and props cannot express that. Children can.

Anatomy

Import the EmptyPanel component and access all parts using dot notation.

import {EmptyPanel} from "@blakeui/pro-react";

<EmptyPanel>
  <EmptyPanel.Header>
    <EmptyPanel.Media />
    <EmptyPanel.Title />
    <EmptyPanel.Description />
  </EmptyPanel.Header>
  <EmptyPanel.Content />
</EmptyPanel>;

Every part is optional. EmptyPanel.Header groups the media and the text so the action area sits at a visibly larger remove than the title sits from its description; drop EmptyPanel.Content when there is nothing to do, and drop EmptyPanel.Media when the words are enough.

The semantics are real. EmptyPanel.Title renders an <h3> and EmptyPanel.Description renders a <p>, so the panel reads as a heading and prose rather than styled spans. The root is not a tab stop, and anything you put in EmptyPanel.Content keeps its own place in the focus order.

Minimal

No matching results

No records match the filters you have applied. Try widening the date range.

Outline

The dashed stroke reads as a drop target rather than a result set, which is what makes it the right treatment for an upload surface. It is utilities on the root — the component ships no outline variant, because the container's treatment belongs to the container.

Drop files to upload

PNG, JPG or PDF up to 10 MB each. You can also pick them from your machine.

Sizes

One scale moves together: a 40 / 48 / 64px medallion, a 20 / 24 / 32px glyph inside it, a 14 / 16 / 18px title, 13 / 14 / 15px prose, and 24 / 32 / 48px of block padding.

Nothing in this folder

Size sm

Nothing in this folder

Size md

Nothing in this folder

Size lg

Full Height

className="h-full" is all it takes. The root already centres on both axes, so the extra height resolves into equal space above and below rather than leaving the block stranded at the top of a tall card.

No data for this period

Once events start arriving, the breakdown for this range will render here.

With Avatar

EmptyPanel.Media is aria-hidden by default, because most empty-state art restates the title and is noise to a screen reader. This avatar names who the panel is about — which the text does not — so it opts out with decorative={false} and variant="default" leaves it unboxed.

ER

Elena has no open tasks

Assign work from the backlog and it will show up on this board.

With Avatar Group

The library ships no avatar group, so the row is composed from real Avatars: a negative inline margin overlaps them and a ring in the page colour cuts each one back out of the one behind it. The group as a whole carries the label; the individual avatars stay hidden inside it, so the row is announced once rather than four times.

No shared boards yet

Your team is set up. Create a board and everyone here gets access to it.

With Background

Two rings on --separator, sized past the medallion and centred on it. They are absolutely positioned, so they add no layout and shift nothing; being inside the media, they inherit its aria-hidden.

You are all caught up

No unread notifications. We will let you know the moment something needs you.

CSS Classes

Base Classes

  • .empty-panel — The root. display: flex in a centred column with gap: var(--empty-panel-gap), block padding, and text-align: center. Declares the six custom properties every part reads, so a size modifier restates values here and nothing else needs to know which size it is in.
  • .empty-panel--sm / .empty-panel--md / .empty-panel--lg — Set the size scale together: a 40 / 48 / 64px medallion, a 20 / 24 / 32px glyph, a 14 / 16 / 18px title, 13 / 14 / 15px prose, a 12 / 16 / 20px gap, and 24 / 32 / 48px of block padding.
  • .empty-panel__header — Groups the media, title and description. Its gap is half the root's, which is what leaves the action area sitting further out than the text is from itself, and max-width: 42ch keeps the prose to a readable measure.
  • .empty-panel__media — The illustration slot. Centres its child and paints nothing by default, so an avatar or an illustration that owns its own shape is not boxed inside a second one.
  • .empty-panel__title — The <h3>. font-size: var(--empty-panel-title-size), medium weight, --foreground ink, text-wrap: balance so a two-line title splits evenly.
  • .empty-panel__description — The <p>. --muted ink at var(--empty-panel-description-size) with text-wrap: pretty, which is what keeps a single word off the last line.
  • .empty-panel__content — The action area. A centred, wrapping flex row with an 8px gap, so a second button drops below the first on a narrow container instead of overflowing.

Data Attributes

  • [data-variant="icon"] — On .empty-panel__media. Adds the circular --default medallion at var(--empty-panel-media-size) and insets the glyph to var(--empty-panel-media-icon-size).
  • [data-variant="default"] — On .empty-panel__media. No background; the glyph takes the full media size and sets its own box.
  • [data-slot] — On every part (empty-panel, empty-panel-header, empty-panel-media, empty-panel-title, empty-panel-description, empty-panel-content), for targeting a part from outside without depending on a BEM class.

Motion

The panel mounts with a 200ms fade and a settle from scale(0.97) on --ease-out, driven by @starting-style — no JavaScript, and no mount flag to thread through. Because the entrance rides the class rather than the component, it survives a render override that swaps the root element.

Reduced motion is gentler, not zero: the fade stays and only the scale settle is dropped. Both signals are honoured — an explicit [data-reduce-motion="true"] on the panel or any ancestor, and @media (prefers-reduced-motion: reduce) — with the media half scoped so the two never double-apply.

API Reference

EmptyPanel

PropTypeDefaultDescription
childrenReactNodeThe panel's content — EmptyPanel.Header and EmptyPanel.Content. Required.
size"sm" | "md" | "lg""md"Media box, type scale, and vertical rhythm. Every part reads it.
render(props) => ReactElementReplaces the root element. Pass the props and ref straight through to a single DOM element; the class, the size scale and the entrance all survive the swap.
classNamestringClass name for the root.

EmptyPanel.Header

PropTypeDefaultDescription
childrenReactNodeThe media, title and description. Required.
classNamestringClass name for the header.

EmptyPanel.Media

PropTypeDefaultDescription
childrenReactNodeAn icon, an avatar, an avatar group, an image. Required.
variant"default" | "icon""default""icon" adds the circular muted medallion and insets the glyph inside it. "default" paints nothing.
decorativebooleantrueHides the media from assistive technology. Turn it off when the media carries meaning the title and description do not already state — an avatar naming who the panel is about — and label it through its own content.
classNamestringClass name for the media slot.

EmptyPanel.Title

PropTypeDefaultDescription
childrenReactNodeThe heading text. Renders an <h3> — the level below the section the panel sits in. Required.
classNamestringClass name for the title.

EmptyPanel.Description

PropTypeDefaultDescription
childrenReactNodeThe supporting prose. Renders a real <p>. Required.
classNamestringClass name for the description.

EmptyPanel.Content

PropTypeDefaultDescription
childrenReactNodeThe actions — buttons, a link, an input. Everything keeps its own semantics and its own place in the focus order. Required.
classNamestringClass name for the action area.

On this page