Empty

What a list looks like when it has nothing in it: a drawing, a sentence saying why, and usually one thing to do about it.

Usage

Empty is what a container shows when it has nothing in it. The props are simple. The work is saying which kind of empty this is, and offering the one thing worth doing about it.

When to use

Empty is the placeholder a container shows when it has nothing to put in it: an illustration, one sentence saying why, and usually one thing to do about it. It sits inside the table, the card or the dropdown that came back with no rows, and takes that container's width.
  • A list, table, card or panel that has no rows yet, where the rest of the screen is still doing its job around it.
  • A search or filter that matched nothing, where the query is still on screen and still editable.
  • A dropdown or popup with no options. Data components already show a small Empty here for you.
Use something else when
  • The whole page is empty. A route with nothing on it is a Result: it owns the viewport, takes a title and a subtitle, and gives the action real weight.
  • The content is still loading. Use Skeleton, or the container's own loading flag. Flashing “No data” while a request runs tells the reader something that was never true.
  • The container's heading already says it. A card titled “Recent activity” does not need a large drawing and “No data” under it. One muted line, or nothing, reads better in a dense dashboard.
  • Something went wrong. A failed request is not an empty one. Say so and offer a retry: Result with status="error", or an Alert above a container that keeps its last good rows.

The three kinds of empty

One component covers all three, and the default text, “No data”, fits none of them. The reader is asking a different question each time, and the description is where you answer it.
  • Nothing yet. The container is new and the reader can fix that, so this is the one kind that earns an action: “No invoices yet” with a Create invoice button under it. It is also the only kind worth a full-size illustration.
  • Nothing matching. The data exists and a filter or query excluded it. Say what was searched and offer the way back: “No results for ‘q4’” with a Clear filters link. A “Create” button here answers a question nobody asked and hides that the rows are one filter away.
  • Nothing you are allowed to see. Rows exist and are not yours. Never let this one fall through to “No data”, which reads as a broken system or invites the reader to create a duplicate. Name the reason and, where there is one, the way to request access.
So the description cannot be a constant. Derive it from the same state that produced the emptiness: is there a query, a filter, a permission answer?

The illustration

image sets how much room the empty state takes. Three values matter.
  • Empty.PRESENTED_IMAGE_DEFAULT is the default, the full illustration. Use it where the empty state is the main thing on its surface.
  • Empty.PRESENTED_IMAGE_SIMPLE is a smaller drawing with tighter spacing. Use it in a card, a table body or anything that has neighbors.
  • A node of your own: an Icon, so the empty state follows the icon pack, or any element. A string is treated as an image URL.
The presets recolor with the theme. A custom image does not, so a flat PNG that reads in light mode can vanish in dark mode. Check it on both.
Size the image with styles.image. The older imageStyle still works but warns in development.

The description

description carries the message. Leave it out and you get the locale's “No data”, which is rarely what you mean.
  • Name the thing that is missing, not the state of the data: “No invoices yet”, not “No data”.
  • One line. If it needs a second, that line is the reason or the next step, and the next step usually belongs in the action slot.
  • Do not apologize and do not blame. “We could not find anything” describes a failure. “No results for ‘q4’” describes a fact.
description={false} removes the text. An empty string still leaves a blank line. Use false inside a tight cell or a dropdown where the illustration is enough.

The action

children renders under the description. Put the next step there when there is one.
  • One action. A second one turns a placeholder into a decision. Where there is a secondary route, make it a link rather than a second button.
  • Match the kind. “Nothing yet” takes a create button. “Nothing matching” takes Clear filters. “Not allowed” takes a request-access link, or nothing.
  • The action has to do what the sentence above it says. An “Add” button under “No results for ‘q4’” leaves the filter in place, so the row the reader just created does not appear.
  • Leave the action out when the reader cannot take it. A create button that opens a permission error is worse than a plain sentence.

Setting it once, with renderEmpty

Data components render an Empty themselves when they have nothing to show, so most empty states in a product are ones nobody wrote. Change them in one place.
  • renderEmpty is a ConfigProvider prop. Setting it once covers Select, Table, Cascader, TreeSelect, Transfer, Mentions and the rest.
  • The callback receives the component name, so one function can answer differently for a dropdown and for a full-page table.
  • Return null for a name to render nothing for it. The default does this for Table.filter, so an empty filter dropdown shows no illustration.
  • ConfigProvider empty={{ image }} only swaps the default illustration for every Empty below it, without replacing the whole render.
For one instance, set locale.emptyText on the data component. It wins over renderEmpty and needs no provider.

Sizing it inside a container

Empty takes the width of whatever it sits in and centers itself. To make it narrower, set a width on the wrapper, not on the Empty.
  • A bare <Empty /> shows the full illustration.
  • Inside Table and List it switches to the simple image, with extra space above and below.
  • Inside Select, TreeSelect, Cascader, Transfer and Mentions it is smaller still, to fit a popup.