02UI

Empty state

An empty state is the screen when there is nothing to show. Four different situations wear this one name, and each one needs a different message, a different action, or no action at all.

Feedback and stateBuild difficulty: Easy<div>

Also called Zero state, Blank slate.

Four unrelated situations get the same design treatment: a list that's genuinely empty, a search that matched nothing, an inbox somebody cleared on purpose, and a page that failed to load. Only three of those are actually this component. The fourth is an error wearing empty clothes, and it's the version that causes the most damage.

Anatomy of an empty state

Anatomy of an empty state: the icon, the heading naming what's missing, the body text giving context, the action that resolves it, and the container filling the space the content would occupy.
  1. 1Icon. Optional, and always decorative. Mark it aria-hidden so it adds nothing to the accessible name.
  2. 2Heading. Names what's missing, specifically. "No projects yet" beats "Nothing here".
  3. 3Body. One line of context or a next step. Skipped when the heading already says enough.
  4. 4Action. Resolves the state: create, clear, or retry. Present for three of the four situations this component covers.
  5. 5Container. Fills the exact space the content would occupy, not the whole page.

The icon is the only optional part. A heading, a line of body text and, in most cases, an action are what actually do the work.

Here it is working. Notice it fills the space the list would use, not the whole page.

No projects yet

Projects you create will show up here.

Create your first project

Notice the dashed edge: it fills the space the project list would use, not the whole page.

When to use an empty state

Use an empty state when

  • A list, inbox, or dashboard genuinely has nothing in it yet, or nothing left in it
  • A search or filter has legitimately returned no matches
  • Someone has cleared every item on purpose and there's nothing left to do

Reach for something else when

Variants

No projects yet

Create your first one to get started.

Create project

No results for "desk lamp"

Try a different search, or clear your filters.

You're all caught up

Nothing left in your inbox.

Only two of the three need an action. The third needs none at all, and it's the one place on the page allowed a little warmth.

First use. Nothing has been created yet. Needs the clearest action of the four, because the person has never done this before: "Create your first project."

No results. A search or filter matched nothing. Needs a way back, not just a description: "Clear filters" or "Clear search", not a restatement of the fact that nothing came back.

All done. Every item was resolved on purpose, an inbox at zero or a task list fully checked off. No action is required, and this is the one place on the page allowed a little warmth.

States

Default
No projects yet
Live update
No results
Momentary
Still loading
Focused action
StateWhat changesWatch out for
DefaultIcon, heading, body, and action fill the space the content would occupyMatch the content's width and height budget so nothing shifts when real data arrives
Live updateReplaces existing results after a search or filter runsNeeds aria-live="polite" so screen reader users hear that the count changed
MomentaryWould show for an instant before a request has actually finishedThe most common bug on this list. Wait for the request to resolve before deciding the list is empty
Focused actionThe button or link inside carries its own focus ringInherits every rule from button, nothing specific to this component

Momentary is the one worth building a test for. A page that renders its empty state first and swaps in real data a second later shows a false empty to everyone on a slow connection, for exactly as long as the request takes.

How an empty state behaves

It fills the content's space, not the page. A dashboard with one empty widget shows the empty state in that widget, with the rest of the layout intact.

It waits for the real answer. Don't render it until the request that would populate the list has actually resolved. Rendering it first and swapping in data is what produces the momentary flash.

A live change announces itself. A first load doesn't. Nothing changed for a reader who just opened the page, so there's nothing to interrupt them with. A search that goes from 12 results to 0 while they're already reading is a change worth announcing.

The action does the smallest useful thing. Create the item, clear the filter, retry the request. A link to a general help article is not a substitute for any of those three.

Empty state accessibility

Announcing a change

A role="status" region with aria-live="polite" on the results container means a screen reader announces the update once a search or filter finishes, without moving focus or interrupting whatever the person is doing.

<div role="status" aria-live="polite">
  <h2>No results for "desk lamp"</h2>
  <p>Try a different search, or clear your filters.</p>
</div>

A static empty state that's simply part of the page on first load doesn't need this. There's no change to announce to someone who hasn't seen anything else yet.

Keyboard

KeyWhat happens
TabMoves to the action, if there is one.
Enter / SpaceActivates it, inheriting whatever a button or a link already does.

There's no keyboard behaviour specific to the empty state itself. Everything interactive inside it is a real button or a real link, and behaves exactly as one.

The rest

  • Heading level. Use whatever level is correct for the empty state's position in the page outline, the same rule a card's title follows.
  • Icon. Decorative in every case observed for this page. Mark it aria-hidden="true" so it doesn't add noise to the accessible name.
  • Contrast. Body text at a normal 14 to 16px still needs 4.5:1 against the background (WCAG 1.4.3), even when it's set in a muted colour to look secondary.

What an empty state should say

Name what's missing, specifically. "No projects yet" beats "Nothing here", because it tells someone what kind of thing they're looking at an absence of.

Match the tone to the variant. First use can invite action. No results can suggest a fix. All done can be warm, briefly, because someone just finished something.

Never blame the person. "You haven't created anything yet" reads as an accusation. "Create your first project to get started" describes the same fact without pointing a finger.

Name the action on the button. "Create your first project", not "Get started", which could mean anything and means nothing once it's read out of context.

Common empty state mistakes

Do

No projects yet

Create your first one to get started.

Names what's missing, explains the fix, and gives the one action that resolves it.

Don't

No projects yet

Please try again later.

The request actually failed to load. Dressed as a first-use empty screen, it hides the failure and drops the one thing that would fix it: a retry button.

Most empty state problems come from one habit: reaching for this component when something actually broke.

1. Rendering a failed request as an empty state. "No results" and "the server didn't respond" look identical to the reader and need completely different fixes. The first needs nothing. The second needs a retry button.

2. A generic heading with no next step. "No data" tells someone what's on screen and nothing about what to do next.

3. No action on a first-use screen. Leaves someone who has never used the product with nowhere to go.

4. An action on an "all done" screen that nobody asked for. Prompting to add another task the moment someone clears their list reads as nagging, not helpful.

5. An illustration large enough to push the action below the fold. The picture is decoration. The button is the point.

6. A flash of empty state before real data has loaded. Render it only after the request that would fill it has actually finished.

7. Language that blames the person for the absence. Describe the state. Don't editorialise about how they got there.

How to build an empty state

<div class="empty-state">
  <svg aria-hidden="true">…</svg>
  <h2>No projects yet</h2>
  <p>Projects you create will show up here.</p>
  <a href="/projects/new" class="button">Create your first project</a>
</div>

For one replacing live search results, add the live region:

<div role="status" aria-live="polite">
  <h2>No results for "desk lamp"</h2>
  <p>Try a different search, or clear your filters.</p>
  <button type="button">Clear filters</button>
</div>

aria-live="polite" waits for the current screen reader announcement to finish before speaking, rather than interrupting mid-sentence the way assertive does. For a state that only changes because someone typed a search or clicked a filter, polite is the correct choice every time.

Edge cases

A returning user versus a first-time one. Someone who deleted their only project sees the same "no projects yet" screen as someone brand new. It's still accurate, and rewriting it based on account history is rarely worth the complexity.

A small widget instead of a full page. Drop the icon entirely once the available space is tight. A short line of text and, if there's room, one action is all a dashboard card has space for.

Restricted access wearing an empty look. A list that's empty because the viewer lacks permission is a different message than a genuinely empty one, closer to an error than an empty state. Say what's actually true: access is restricted, not that there's nothing there.

Deleting the last item on a page deep in pagination. If someone removes the final item on page 4, the page needs to redirect to a valid page or show its own empty state, rather than rendering a page number that no longer has any content.

Empty states in real products

Linear's empty issue backlog pairs "Create your first issue" with the actual keyboard shortcut that does it, which turns the empty state into a genuine onboarding hint rather than a dead end.

Slack's "You're all caught up" for a fully-read channel is the all-done variant with the tone this page argues for: no action, a small amount of warmth, and nothing more.

Google Drive's no-results search screen suggests checking the spelling or trying different words, giving the person a next step instead of just confirming that the search came back empty.

Common questions

Is an empty state the same as an error state?
No, and treating them the same is the most common mistake on this page. Empty means the request worked and there's genuinely nothing to show. Error means the request failed. Showing a friendly empty-state message when a server call actually failed hides the problem and removes the one thing that would fix it: a retry button.
Does every empty state need a call to action?
Three of the four do. First use needs a way to create the first item. No results needs a way to clear the search or filter. Error, which shouldn't really be built as an empty state at all, needs a retry. Only the fourth, a list someone emptied on purpose, is fine with no action at all.
Does an empty state need an illustration?
No. A heading, a line of body text, and an action cover every case. An illustration is decoration, and decoration that pushes the actual button below the fold has cost more than it added.
How does a screen reader user know a search returned zero results?
The result region needs `aria-live="polite"` so the announcement fires when the count changes. A static empty state present from the first page load needs nothing extra, because nothing changed for a reader arriving fresh.
What should the button say?
Name the action. "Create your first project" beats "Get started", because it says exactly what pressing it does, and it still makes sense pulled out of context into a screen reader's list of controls.

Last reviewed 29 JUL 2026