Skip to content
FoundationsLesson 9 of 9Beginner10 min

Empty states & onboarding

The zero-data screen is a designed moment, not an accident: name the space, explain what appears here, and offer one clear path to a first success.

Kept on this device only.

In one line: the first screen a new user sees is usually empty — treat it as onboarding, not a blank "No items," and give them exactly one obvious way to reach their first win.

What it is

An empty state is what a feature shows before there's any data: the new inbox, the projects list with no projects, the search with no results yet. It's the most-seen screen for brand-new users and a recurring one for everyone (deleted everything, no results, an error).

There are really three distinct zero-data screens, and conflating them is a common bug: first-run (nothing yet, by design), no-results (a filter or search matched nothing), and error (we failed to load). Each wants different words and a different next step.

Why it matters

The first-run empty state is your onboarding whether you designed it or not. A blank "No items" is a dead-end: it tells the user nothing about what the feature is for or how to start, so the most motivated moment — they just arrived — is wasted.

A designed empty state does three jobs: it orients (what is this space?), motivates (why is it useful?), and activates (one clear action toward a first success). That single CTA is the highest-leverage button in the product; it converts curiosity into the habit-forming first action.

See it

Live demo
Tweak it2
Compare a blank dead-end to a designed first-run and an error state.

Compare the blank dead-end with a designed first-run state and an error state. Notice the designed version names the space, sets an expectation, and offers exactly one action.

How it works

A good first-run empty state has a tight anatomy:

  • A heading that names the space in plain words ("Your inbox is clear").
  • One sentence on what will appear here and why it matters.
  • Exactly one primary action — more than one and you've diffused the very focus an empty state exists to create.
  • A quiet visual — an icon or small illustration, decorative (so aria-hidden), to make the screen feel intentional rather than broken.

Keep the three states distinct. No-results should echo the query and offer to clear filters, not invite a "create" action. Error should say what failed, reassure that data is safe, and offer a retry — never masquerade as "empty."

Build it

HTML
A first-run empty state: orient, motivate, activate
<section class="empty">
  <!-- Decorative; hidden from assistive tech. -->
  <svg class="icon" aria-hidden="true" viewBox="0 0 24 24"><!-- inbox glyph --></svg>
 
  <h2>Your inbox is clear</h2>
  <p>New conversations land here. Start one to see how threads are organized.</p>
 
  <!-- Exactly one primary action toward first success. -->
  <button type="button" class="cta">New message</button>
</section>

Make it yours

Use the controls beside the demo above to change treatment and show call to action — each change updates the example live.

Experiment in the playground
  • Switch to blank: no heading, no orientation, no action — the version that quietly loses new users.
  • Toggle the CTA off and feel how much weaker the designed state is without a single clear next step.
  • Switch to error: same empty screen, completely different words — it reassures and offers retry, it doesn't say "nothing here."

Reproduce it with an LLM

Reproduce it with an LLM

You are a senior product designer-engineer. Design the empty state for a feature — the screen a user sees before there's any data. Don't ship a blank 'No items' dead-end: include a short heading that names the space, one sentence that explains what will appear here and why it's useful, and exactly one primary call to action that gets them to their first success. Add a quiet illustration or icon (decorative, `aria-hidden`). Use correct heading semantics, keep the CTA a real focusable button, and make sure the state is distinct from a loading state and an error state. Return only the component code with imports.

Pitfalls & accessibility

Further reading