Skip to content
Bugwise
Menu
UI Components

Empty & Error States

What a screen shows when there's nothing to show, or when something failed. Learn the six types, what a good one says, how testers force them with fresh accounts, airplane mode and proxy tools, and the spinner, flash and Retry bugs they find.

35 min Feedback & overlays iOS · Android · Web Also called: Empty state, Zero state, Error screen, Blank slate

Designers spend weeks on the screen full of beautiful data. Then a real user signs up, opens Orders for the first time and sees… a white screen. Or a spinner that never stops. Or Error 500. The “nothing here” and “something broke” screens are often designed last, built in a hurry, and tested by nobody.

That makes them a gift for testers. Every list, feed, search and dashboard in an app has these states, and you can trigger most of them in a minute if you know how.

What are empty and error states?

An empty stateEmpty stateThe screen a user sees when there is no content yet, like an empty cart or no search results.Open in glossary → is what a screen shows when it has no content to display. An error state is what it shows when it couldn’t get or show the content. Together with the loading stateLoading stateWhat the UI shows while it waits for data or an action to finish, such as a spinner or skeleton.Open in glossary → and the normal data state, they make up the UI statesUI stateHow a component looks and behaves in a given situation: default, hover, pressed, focused, disabled, loading, error.Open in glossary → that every data screen must handle.

The key idea: “empty” and “failed” are different situations and need different messages. “You have no orders” and “We couldn’t load your orders” might both show an empty list, but they mean opposite things to the user.

The six types

Orders

No orders yet

When you order food, you'll find your orders and receipts here.

First use

A brand-new user, nothing created yet.

Create it: Sign up with a fresh test account.

Inbox

All caught up

You've read every message. New ones will appear here.

User cleared

The user finished or deleted everything.

Create it: Delete, archive or complete every item.

Search

No results for "sushi vegan gluten-free"

Check the spelling or remove a filter.

No results

A search or filter matches nothing.

Create it: Search for "zzqx" or combine filters that exclude everything.

Team orders

Only admins can see team orders

Ask an admin of Acme Ltd. to give you access.

No permission

Data exists, but this user may not see it.

Create it: Log in with a lower role, or deny an OS permission.

Orders

You're offline

Check your connection. We'll reload as soon as you're back.

Offline

The device has no connection.

Create it: Airplane mode, Wi-Fi off, or a proxy that blocks requests.

Orders

We couldn't load your orders

This is on our side. Please try again in a moment.

Error

The server or the app failed.

Create it: Map local / breakpoint a 500, a timeout or broken JSON.

Loading vs empty vs error

A screen that loads data goes through a few states. It should show exactly one at a time:

StateWhat it meansWhat the user should see
LoadingRequest on its waySkeleton or spinner, ideally in the same layout as the content
DataRequest OK, items returnedThe content
EmptyRequest OK, zero itemsAn empty state with a next step
ErrorRequest failed (offline, timeout, 500, 403)An error state with a way to recover

Most bugs on these screens are mix-ups between these four: an empty list treated as “still loading” (endless spinner), an error treated as “empty” (the user thinks their data is gone), or “empty” shown for a moment before the data arrives.

Anatomy of a good one

A good empty or error state answers three questions: what happened, why, and what can I do now?

  1. 1

    Illustration or icon

    Sets the tone at a glance (offline, nothing here yet, locked). Decorative, so screen readers skip it.

  2. 2

    Headline: what happened

    Plain words, no codes: "We couldn't load your orders", not "Error 503".

  3. 3

    Body: why, and what next

    One or two sentences. Says whose problem it is (yours or ours) and what the user can do. No blame, no jargon.

  4. 4

    Primary action

    The one thing that fixes it: Retry, Browse restaurants, Clear filters. It must actually work.

  5. 5

    Secondary action (optional)

    A way out when the main fix fails: Contact support, Go to Home, Switch account.

  6. 6

    Surrounding UI stays usable

    The navigation and header still work, so the user is never trapped on the error.

Designs & variants

Full-screen vs inline

Not every problem deserves a whole screen. Match the size of the message to the size of the problem.

Inline: only one part failed

Sakura Sushi Bar

On the way · arrives 19:35

Couldn’t load recommendations.Retry

The order still shows. Only the failed section has a small message and its own Retry.

Full-screen error for a small failure

Something went wrong

Please try again later.

The order tracking worked, but one failed widget wiped out the whole screen.

Use a full-screen state when the screen’s main content is missing (the whole list failed, no permission, offline with no cached data). Use inline messages for one section, one card or one form field, and a banner for ongoing conditions like “You’re offline” while cached content stays visible.

Wording: say it like a human

The words matter as much as the picture. A good error messageError messageText that tells the user something went wrong and, ideally, how to fix it.Open in glossary → is specific, calm and useful:

Plain, specific, actionable

We couldn’t load your orders This is a problem on our side, not yours. Please try again in a moment. [Try again]

Codes, blame or dead ends

Error 500 TypeError: Cannot read properties of undefined (reading 'map') Invalid request. You did something wrong.

Rules to test against:

  • No raw codes or stack tracesStack traceThe list of function calls printed when an error happens, showing where in the code it occurred.Open in glossary →. “500”, “-1009” or “TypeError” mean nothing to users and can leak technical details to attackers. A short reference ID for support (“Ref: 8F3K”) is fine.
  • No blame. “Invalid input” or “You did something wrong” for a server problem is both rude and false.
  • Say whose problem it is. “Check your connection” (user can fix it) vs “This is on our side” (user should wait).
  • Always a next step. Retry, Clear filters, Go to Home, Contact support. Never a dead end.
  • Don’t lie. An error shown as “No orders yet” makes users think their data is gone.

Skeletons vs spinners

While data loads, a skeleton (grey shapes in the layout of the real content) feels faster and avoids layout jumps. A spinner is fine for short waits or small areas. Either way, test that the loading state ends: with data, with an empty state, or with an error. A loader that can spin forever is a bug.

Loading (skeleton)

Loaded

  • Sakura Sushi Bar2 items · €24.80 · Today, 19:05On the way
  • Napoli Pizza Corner3 items · €31.50 · Sat, 20:12Delivered
  • Green Bowl1 item · €11.90 · Thu, 12:40Delivered

Try it: state explorer

Here’s one Orders screen in seven states. Switch between them and compare the bad version (things testers report every week) with the good one. In the good version, Retry and Show all orders really work.

Screen state

How testers create it: Sign up with a fresh account that has never ordered.

Bad

Orders

AllActiveCanceled

No data

  • "No data" is technical and cold
  • No explanation and no next step
  • Looks like an error

Good

Orders

AllActiveCanceled

No orders yet

When you order food, you'll find your orders and receipts here.

  • Says what will appear here
  • One clear action: Browse restaurants
  • Friendly, not alarming

When to use it

Every screen that shows data needs an empty and an error state. Ask for them in reviews if the design only shows the “happy” screen. In particular:

  • Lists, feeds and grids: orders, messages, favorites, notifications, search results.
  • Screens behind an account or role: team data, admin pages, shared documents.
  • Anything that needs the network: which is almost everything.
  • Features that need a permission: camera, photos, location, contacts. If the user said no, explain why the feature needs it and how to turn it on in Settings.

Good and bad uses

Banner while cached content stays

You’re offline. Showing saved orders.
  • Sakura Sushi Bar2 items · €24.80 · Today, 19:05On the way
  • Napoli Pizza Corner3 items · €31.50 · Sat, 20:12Delivered

The user can still read their orders.

Full-screen offline over good data

No connection

Connect to the internet to continue.

The orders were already loaded, but the app hides them the moment the signal drops.

Also don’t use a big empty-state illustration for tiny areas (an empty “Notes” field just needs placeholder text), and don’t use an error state for validation problems in a form: those belong next to the field (see Text field).

Common bugs

#BugWhat you seeTypical severity
1Endless spinnerEmpty list or failed request, but the loader never stopsHigh
2Dead-end error“Something went wrong” with no Retry, no explanation, no way backMedium–High
3Retry is brokenRetry does nothing, or sends 2+ requests per tap (duplicate orders on POST!)Medium–High
4Raw technical errorTypeError, 500, null, undefined or a stack trace on screenMedium
5Empty flash“No orders yet” appears for a moment before the data loadsLow–Medium
6Error shown as emptyOffline, 403 or 500 shows “No orders yet”, so users think data was lostHigh
7Wrong empty messageFiltered list says “Place your first order” instead of “No results for this filter”Low–Medium
8Stale error“You’re offline” stays after reconnecting, or the error stays after a successful retryMedium
9Two states at onceData and the empty message both visible, or spinner on top of an errorLow–Medium
10Silent for screen readersError appears but VoiceOver / TalkBack says nothing; focus stays on a removed buttonHigh (for affected users)

Find the bug

This food delivery app’s Orders screen has six bugs. Use the device and proxy panel like a real tester:

  • Watch the screen closely while it loads, then tap Refresh.
  • Try each filter chip.
  • Set Map Local to an empty list, then to a 500 error, and tap Refresh each time.
  • Turn on Airplane mode, tap Refresh, turn it off again and tap Retry. Watch the network log.

🐞 Bug hunt: Orders screen

This demo has 6 hidden bugs. Explore it like a tester, then check your findings.

Found0/6
9:41

Orders

No orders yet

When you order food, you'll find your orders and receipts here.

Device & proxy

Changes apply to the next request: tap Refresh in the app after changing them.

Network log

  • Waiting for requests…
  1. 1

    Hint: Set Map Local to "200 · empty list []" and tap Refresh.

  2. 2

    Hint: Set Map Local to "500 server error" and tap Refresh.

  3. 3

    Hint: Go offline, Refresh, then tap Retry once and count the requests in the network log.

  4. 4

    Hint: Tap Refresh (or Restart app) and watch the first half second.

  5. 5

    Hint: Turn Airplane mode on, then off, then load the orders again.

  6. 6

    Hint: Tap each filter chip.

How to test it

How to create each state

You can’t wait for a real outage. Testers force these states on purpose:

  1. First use. Create a fresh test account (or clear the app’s data / reinstall) and visit every list screen before doing anything else.
  2. User cleared. Delete, archive or complete every item: empty the cart, read all notifications, remove all favorites.
  3. No results. Search for nonsense (“zzqx”), combine filters that exclude everything, or filter a date range with no data.
  4. No permission. Log in with a role that has fewer rights, open a link to someone else’s data, or deny an OS permission (camera, photos, location) when asked. See authorizationAuthorizationChecking what a logged-in user is allowed to see or do.Open in glossary →.
  5. Offline. Turn on airplane modeAirplane modeA device setting that turns off all wireless connections at once.Open in glossary → before opening a screen, and also while it loads. Then reconnect and see what recovers by itself. More in Network conditions.
  6. Server errors and edge responses. With a proxy toolProxy toolA program that sits between the app and the server so you can watch and change network traffic.Open in glossary → like Charles or Proxyman, use Map LocalMap LocalA proxy rule that answers a request with a file you choose instead of the real server response.Open in glossary → to return [], a 500, a 403, an empty body or broken JSON, or a breakpoint to delay a response by 30 s to trigger a timeoutTimeoutThe time limit after which an app stops waiting for a response and gives up.Open in glossary →. Step by step in Proxy tools.

Checklist

Empty & error state checklist

0 of 12 checked

Example test cases

TC-EES-001First-use empty state for a new account

Preconditions

  • Brand-new account with no orders

Steps

  1. Log in
  2. Open the Orders tab

Expected result

After a short loading state, "No orders yet" is shown with an explanation and a "Browse restaurants" button that opens the restaurant list.

TC-EES-002Empty API response ends the loading state

Preconditions

  • Proxy Map Local: GET /api/orders returns 200 with body []

Steps

  1. Open the Orders tab
  2. Wait 10 seconds

Expected result

The spinner stops within the normal response time and the empty state is shown.

TC-EES-003Server error shows a recoverable error state

Preconditions

  • Proxy Map Local: GET /api/orders returns 500

Steps

  1. Open the Orders tab
  2. Read the message
  3. Turn off Map Local
  4. Tap Try again

Expected result

A friendly message with no codes or technical text and a Try again button. After Try again, exactly one request is sent and the orders appear.

TC-EES-004Offline and back online

Preconditions

  • Orders were loaded at least once

Steps

  1. Turn on airplane mode
  2. Pull to refresh on Orders
  3. Turn off airplane mode
  4. Wait 5 seconds

Expected result

While offline, a "You're offline" banner is shown and the saved orders stay visible. After reconnecting, the banner disappears and the list refreshes without user action.

TC-EES-005Filter with no results

Preconditions

  • Account has orders, none of them canceled

Steps

  1. Open Orders
  2. Tap the "Canceled" filter

Expected result

"No canceled orders" is shown with a "Show all orders" action. Tapping it shows all orders again.

TC-EES-006No permission is not shown as empty

Preconditions

  • Logged in as a team member without the admin role
  • Team has orders

Steps

  1. Open Team orders

Expected result

The screen explains that only admins can see team orders and offers "Request access". It does not say "No orders yet".

TC-EES-007No empty-state flash on a normal load

Preconditions

  • Account with orders
  • Screen recording on (to slow-motion review)

Steps

  1. Force-close the app
  2. Open it and go to Orders
  3. Review the recording frame by frame

Expected result

Only the loading state and then the orders appear. The empty state is never visible, not even for one frame.

TC-EES-008Error state is announced by the screen reader

Preconditions

  • VoiceOver or TalkBack is on
  • Map Local returns 500

Steps

  1. Open the Orders tab
  2. Wait for the error

Expected result

The screen reader announces the error message, and swiping reaches the Try again button. After Try again succeeds, focus lands on the list or its heading, not on nothing.

Tip: click a test case to expand it.

Accessibility

  • Announce state changes. When loading ends in an empty or error state, screen reader users must hear it. On iOS and Android, the app should post an accessibility announcement or move focus to the message.
    Dev note
    On the web, put the message in a live region (role="status" for empty, role="alert" for errors that need attention).
  • Manage focus after Retry. The Retry button disappears when content loads. Focus should move to the content (or its heading), not get lost at the top of the page.
  • The illustration is decorative. It should be hidden from screen readers. The text carries the meaning.
  • Loading needs words too. A spinner should have an accessible label like “Loading orders”, and the skeleton itself should be hidden from screen readers.
  • Don’t rely on color or icons alone. A red cloud icon without text tells a color-blind or blind user nothing.
  • Contrast and text size. Grey “helper” text in empty states is often too light: it still needs 4.5:1, and must not be cut off at large text sizes.
  • Buttons are real buttons with at least 44×44 pt / 48×48 dp touch targets and visible keyboard focus on the web.

Mobile vs web

  • Many apps follow the system layout for empty and “no results” states: icon, title, description, optional action.
    Dev note
    SwiftUI has a built-in ContentUnavailableView for them (iOS 17+).
  • Test with airplane mode from Control Center, and with Low Data Mode and Wi-Fi with no internet.
  • Check that denied permissions (Photos, Location) explain how to re-enable them in Settings, ideally with a button that opens the app’s settings page.
  • Use the Network Link Conditioner (developer setting) for “100% Loss” and very slow profiles.
  • Material guidance: keep empty states simple, with a short message and, where it helps, one action. Pull-to-refresh is a common recovery path, so test it on error screens too.
  • Test with airplane mode, mobile data off for this app (per-app data restriction), and Data Saver.
  • On the emulator, use the extended controls (Cellular settings) to simulate no signal or slow networks.
  • Rotate the device and resize on tablets and foldables: the empty state should stay centered and readable.

Check your understanding

Quick quiz

0/6 answered · 0 correct

  1. 1.The API returns 200 with an empty list [], but the Orders screen keeps spinning. What kind of bug is this?

  2. 2.Which error message is best?

  3. 3.A team member without admin rights opens Team orders and sees "No orders yet", but the team has 40 orders. What is wrong?

  4. 4.How can a tester make the Orders API return a 500 error on demand?

  5. 5.You reconnect after being offline, but the "You're offline" banner stays. What should you check next?

  6. 6.For a loading list, what is usually better than a blank screen?

Key takeaways

  • Every data screen has four states: loading, data, empty and error. Exactly one should be visible at a time.
  • Empty comes in types (first use, user cleared, no results, no permission), and errors in kinds (offline, server error, timeout). Each needs its own message.
  • A good state says what happened, why, and what to do next, with a Retry or action that really works, and never shows raw codes or blames the user.
  • Force the states yourself: fresh accounts, deleting everything, airplane mode, and proxy tools returning [], 500 or slow responses.
  • Always test the way back: reconnect, retry, clear the filter, and check the screen fully recovers.

Finished this page?Nice work, this page is complete!

Mark it as done to track your progress. It's saved in this browser, no account needed.

Its review cards are now in Bites ⚡
Esc

Type to search the whole site.