Adamarant
Start
Back to Field notes

Empty state design: the 4 states every screen needs

Product DesignAug 30, 20269 min read

A screen needs four empty states: first use, no results, cleared, and error. Baymard finds close to half of sites leave the no-results screen a dead end.

Empty white room with skylights and concrete floor

At the end of this you will have four empty states written for every list, table, and dashboard in your product: first use, no results, cleared, and error. Each one with a title, one action, and a message a screen reader reads out when the state changes. It takes about an afternoon per screen, and it is the cheapest onboarding work available to a product team.

An empty state is the screen a product renders when there is no data to show. Atlassian's design system defines it as the view that appears when there is no data and describes what the user can do next. The second half of that definition is where teams lose. Most ship one "No items found" and let it stand in for four situations that need four different answers.

What you need before you start

  • A list of every screen in your product whose content comes from a query: lists, tables, dashboards, search results, side panels, activity feeds, notification trays.
  • Whoever writes your microcopy. An empty state is mostly writing, so putting this on an engineer alone produces "No data available" on twenty screens.
  • One illustration decision, made once for the whole product: draw them, buy them, or ship none. Three illustration styles across ten screens read worse than none at all.
  • A screen reader. VoiceOver on macOS, NVDA on Windows. Step 6 cannot be verified by reading code.

Step 1: Inventory every screen that can render nothing

Walk the product and write down every surface fed by a query. For each one, ask the question IBM's Carbon design system puts at the centre of its empty states pattern: what do the pages, tiles, data tables, and side panels look like without content? A mid-size B2B tool usually has between 15 and 40 of them. Half have never been seen empty by anyone on the team, because seed data hides them in development.

Two ways to find them fast. Point a local build at an empty database and click through every route. Then query production for accounts with zero rows in the main entity: those users are looking at your empty states right now, and they are the ones deciding whether to come back.

Step 2: Split the one empty state into four

The four states have different causes, so they need different copy and different actions.

  • First use. The account is new and this table has never held data. The job is to say what will appear here and how to put it there.
  • No results. A search or a filter returned nothing. The data exists, the query missed it. The job is to get the user back to results.
  • Cleared. The user archived, completed, or deleted everything. That is a success, and the copy should say so before offering anything else.
  • Error. The request failed. The job is to say what failed and offer a retry.

The error case is the one most often folded into the others, and it is the one that does real damage. A failed request rendered as "You have no projects yet" tells a paying user their work is gone. Keep the paths apart in the component itself: a variant prop with four values costs an hour and makes the mistake impossible.

Step 3: Write the title as a positive statement

Carbon's guidance on empty state titles is short and worth copying exactly: write it as a positive statement wherever you can. "Start by adding data assets" reads better than "You don't have any data assets". Where the negative is unavoidable, the word "yet" does most of the repair.

Three rules that hold across every empty state we ship:

  • The title states the situation or the next step in under eight words.
  • The body explains what the user gets from taking the step, not the mechanics of the button underneath it.
  • No apology. "Sorry, there's nothing here" spends the reader's attention on your feelings about an empty table.

Step 4: Give one action, placed where the action lives

One primary action per empty state. A secondary link to documentation is fine. A second button is a choice the user cannot make, because they have never used the feature.

Carbon lists three ways to present that action: a button under the copy, a link inside the copy, or an instruction pointing at the real control in the interface. The third is underrated. Pointing at the "New project" button in the toolbar teaches where the control lives, so the second time around the user does not need the empty state at all. Use it when the control is on screen. Use a button when it is not.

Step 5: Treat "no results" as a recovery problem

This is the state with public research behind it. Baymard Institute's ecommerce search benchmark found that close to half of sites fail to give users a usable way back after a search returns nothing, which turns the screen into a dead end and raises abandonment.

Recovery is concrete work, not a friendlier sentence:

  • Show the query that was run, so the user can see the typo.
  • Keep the search input filled and focused, so correcting it takes one keystroke.
  • Offer the nearest thing you do have: the same search with one filter dropped, or the unfiltered list, with a count.
  • Name the scope. "No invoices match 'acme' in 2024" tells the user which of the three things to change.

Nielsen Norman Group's guidance for "no results" pages points the same way. Generic search tips are the weakest thing you can put on this screen: by the time a user reads "check your spelling", they have already checked it.

Step 6: Make the state audible to a screen reader

An empty state that appears after a filter change is a status message under WCAG success criterion 4.1.3, Level AA. The content changed without a page load and without focus moving, so assistive technology has to be told in words.

What that means in the markup:

  • The live region has to be in the DOM before the content changes. Mounting the container and its message in the same render often announces nothing at all.
  • Use role="status" for the result count. It carries an implicit aria-live="polite", so the announcement waits for a pause instead of cutting the user off mid-sentence.
  • Announce the fact, not the decoration. "No invoices match this filter" is the message.
  • Empty state illustrations are decorative, so they get an empty alt attribute per W3C's decorative images guidance. Carbon recommends the empty alt over role="presentation", on support grounds.

Step 7: Keep loading out of it

A screen that is still fetching is not empty. Rendering "No projects yet" for 400 milliseconds while the request is in flight teaches a new user that the product is broken, and it happens on every connection slower than the one in your office. Render a skeleton while the request is pending, then switch to the empty state once the response confirms zero rows. Shopify's Polaris splits the two the same way: skeleton components for the loading pass, the empty state component for the answer.

How to check it works

Five checks per screen, about twenty minutes each.

  1. Create a fresh account with no data and walk every route. Screenshot each empty screen you hit.
  2. Search for a string that cannot match anything, then get back to results in one click. If you cannot, step 5 is not done.
  3. Delete everything on one screen and read the result out loud. It should sound like a finished task, not a failure.
  4. Block the request in devtools. The screen should say the request failed and offer a retry, never "you have nothing yet".
  5. Turn on VoiceOver or NVDA and apply a filter that returns nothing. If nothing is spoken, step 6 is not done.

Common failures and how to fix them

  • The error dressed as first use. One catch block, one generic component, and a 500 becomes "Create your first invoice". Fix it at the data layer: distinguish an empty result from a failed request before the component ever renders.
  • An action the user cannot take. A viewer-role account reads "Create your first project" and has no create permission. Check the permission before rendering the button, and swap the copy for what this user can actually do, which is usually asking an admin.
  • The illustration carrying the message. If removing the drawing removes the meaning, the copy is not finished. Write the state so it works in plain text first, then add the picture.
  • An empty dashboard. A dashboard with eight zeroed charts is the worst first screen in software. Carbon's alternative is starter content: sample data the user can explore and then clear. It costs real product work, so reserve it for the primary screen and leave basic empty states on the secondary ones.
  • Copy written once, then abandoned. Empty states name features, and features get renamed. Put them in the same review pass as your onboarding copy, not in a separate backlog nobody opens.

Where this connects

Empty states are the cheap half of activation work. The expensive half is the flow around them, covered in SaaS onboarding UX patterns that cut first-week churn. If the screen you are fixing is a dashboard, the density decisions come first: see SaaS dashboard design. And step 6 is one line item in a much longer list, which we walk through in accessibility-first design after the EU Accessibility Act.

Sources

Photo by Julia Taubitz on Unsplash

Frequently asked questions

How many empty states does a single screen actually need?+

Most list and table screens need three: first use, no results, and error. Add the cleared state when the screen holds items a user can finish or archive, such as a task list, an inbox, or an approval queue. A dashboard is the exception, because a zeroed chart is not really an empty state and needs either starter content or a different first screen. If you are building the component once for the whole product, ship all four variants and let each screen pick the ones it can reach.

Do empty states need an illustration?+

No, and shipping none beats shipping three styles. An illustration helps when it shows what the populated screen will look like, which is why sketched previews of a filled table work better than a generic mascot. It hurts when it carries the message: if removing the drawing removes the meaning, the copy is unfinished. Whatever you choose, the illustration is decorative markup with an empty alt attribute, so it stays out of the screen reader path.

What is the difference between an empty state and a skeleton screen?+

A skeleton says the answer is coming. An empty state says the answer arrived and it is zero. They cover different moments, so showing the empty state while a request is still in flight is a bug, not a style choice. In practice the component needs three inputs rather than one boolean: pending, error, and row count. Products that store only "is there data" end up flashing "nothing here" at every user on a slow connection.

Studio

Start a project.

We write about what we build. Tell us what you want to build.