Empty States Are the Feature: Designing Zero-Data Screens That Actually Convert
Empty states are the first product experience most users see, and most teams treat them like a 404. Here's how to design zero-data screens that teach, activate, and quietly do the heavy lifting for onboarding.

Every product has a moment where the user stares at nothing. No projects, no messages, no data — just a blank canvas and a decision: figure this out, or leave. Most teams ship a shrug emoji and a grey illustration for that moment, then wonder why activation is flat.
Empty states are not decoration. They're the shortest, highest-leverage piece of onboarding you own, and they get designed last, by whoever has time, with copy written in Slack five minutes before release. This piece is about treating them like the feature they are.
Why empty states punch above their weight
A new user's first session is the only session where you have their full attention and zero context to lean on. There is no history, no personalization signal, no prior action to nudge. What fills that vacuum is the empty state — the dashboard with no widgets, the inbox with no threads, the analytics view before any events fire.
In our experience across SaaS and marketplace builds, the gap between a well-designed empty state and a lazy one shows up in three places:
- Time-to-first-action. Users hit a meaningful action faster when the empty state tells them what the action is.
- Support ticket volume. "How do I get started?" tickets drop sharply when the zero-data screen answers that question inline.
- Trial-to-paid conversion. Products with clear activation moments convert better, and activation almost always passes through an empty state.
The frustrating part: none of this requires new engineering. It requires taking the screen seriously.
The four kinds of empty, and why they need different designs
One of the reasons empty states feel generic is that teams design an empty state, singular, and reuse it. But there are at least four distinct zero-data situations, and each one wants a different tone and call-to-action.
First-run empty (the user has never done this)
This is onboarding. The user has no idea what a "project" or "pipeline" or "segment" is in your product's specific vocabulary. The job of the screen is to teach and lower activation energy. Show a preview of what a filled state looks like, offer a one-click sample, and put the primary action front and center.
User-cleared empty (they finished everything)
Inbox zero, all tasks done, no pending approvals. This is a win state, not a failure state. Celebrate briefly, then offer a secondary useful action — review archived items, invite a teammate, tune notifications. Do not guilt-trip the user into creating more work.
Filtered empty (their query returned nothing)
A search or filter matched no records. The screen must clearly communicate that the data exists elsewhere and the filter is the reason nothing shows. Offer a one-click "clear filters" action. This is the empty state most likely to be mistaken for a bug.
Error-adjacent empty (something failed silently)
API returned an empty array because auth expired, or a downstream service is degraded. Don't disguise this as a normal empty state. Users will sit there wondering why their data vanished. Distinguish it visually and offer a retry.
One dumb but useful test: read your empty-state copy out loud in each of these four scenarios. If it sounds off in any of them, you're reusing a screen that shouldn't be reused.
Anatomy of an empty state that earns its space
Stripping the decoration away, a good first-run empty state has five parts:
- A headline that names the concept — not "Nothing here yet" but "Your first campaign lives here."
- One sentence of context — what this thing is and why the user wants one.
- A primary action — the fastest path to a non-empty state.
- A secondary path — usually "try a sample" or "import from..." for users who aren't ready to start from scratch.
- A visual anchor — a diagram, a faded preview of the populated state, or a simple illustration. Not a stock rocket ship.
Here's a component sketch we tend to reach for. It's deliberately un-fancy because most of the value is in the copy and the actions, not the chrome:
type EmptyStateProps = {
variant: 'first-run' | 'cleared' | 'filtered' | 'error';
title: string;
description: string;
primaryAction: { label: string; onClick: () => void };
secondaryAction?: { label: string; onClick: () => void };
preview?: React.ReactNode; // faded example of populated state
};
export function EmptyState({
variant,
title,
description,
primaryAction,
secondaryAction,
preview,
}: EmptyStateProps) {
return (
<section
role="status"
aria-live={variant === 'error' ? 'assertive' : 'polite'}
className="flex flex-col items-center gap-4 py-16 text-center"
>
{preview && (
<div aria-hidden="true" className="opacity-40 pointer-events-none">
{preview}
</div>
)}
<h2 className="text-lg font-semibold">{title}</h2>
<p className="max-w-md text-sm text-neutral-600">{description}</p>
<div className="flex gap-3">
<button onClick={primaryAction.onClick} className="btn-primary">
{primaryAction.label}
</button>
{secondaryAction && (
<button onClick={secondaryAction.onClick} className="btn-ghost">
{secondaryAction.label}
</button>
)}
</div>
</section>
);
}
A few things worth noting:
- The
variantprop forces the caller to declare which kind of empty this is. That alone kills a category of design drift. aria-livediffers by variant so screen readers get the right urgency when the state changes mid-session (a filter clears, an error fires).- The
previewslot isaria-hiddenbecause it's decorative; the headline and description carry the meaning.
Copy is the whole game
Designers love the illustration part. Engineers love the state-machine part. Both under-invest in the twelve words that actually change behavior.
A few rules we've landed on after enough teardowns to be opinionated:
- Name the noun in your product's vocabulary. "No boards yet" beats "No items found" every time. It teaches the domain.
- Lead with the outcome, not the mechanism. "Track a shipment to see live status" beats "Add a shipment record."
- Never apologize for the empty state. "Oops!" and "Sorry, nothing here" make users think something broke.
- The button label should complete the sentence "I want to..." — "Create your first board" is fine, "Submit" is a crime.
- Filtered empty states must acknowledge the filter. "No results for 'invoice 2024'" is a completely different message from "No invoices yet."
If you can only fix one thing in your empty states this quarter, fix the copy. It's the cheapest lever and the one with the biggest delta.
Accessibility, motion, and the details engineers care about
Empty states are a favorite hiding spot for accessibility regressions. A few checks that catch most of them:
- Announce state transitions. When a list goes from populated to empty (a filter change, a bulk delete), the empty state should be inside a live region so assistive tech announces it. Otherwise the screen appears silent.
- Do not rely on color or illustration alone. The error-adjacent empty state must be distinguishable in text, not just via a red icon.
- Contrast the muted preview correctly. A faded populated-state preview is decorative only if it's marked
aria-hiddenand not focusable. If any element inside is a real button, you now have a keyboard trap for a screen the user can't see. - Respect
prefers-reduced-motion. If your empty state has an animated illustration to draw the eye, disable it for users who've opted out. A static equivalent should carry the same weight.
On motion specifically: a small, one-shot entrance animation on the primary CTA can meaningfully improve click-through, but only if it fires once per session. Looping animations on empty states are visual noise and pull attention away from the copy that's doing the actual work.
Instrument them like features
If empty states are onboarding, you should be measuring them. At minimum, log:
- Which empty-state variant rendered, and where.
- Whether the primary or secondary action was clicked, dismissed, or ignored (session ended on this screen).
- Time from empty-state view to first successful populated state.
That third metric is the one that matters. It's a proxy for activation, and it lets you A/B copy changes with confidence. We've seen a rewritten headline and a clearer primary action move that metric more than a full onboarding tour revamp — for a fraction of the engineering cost.
A warning: don't treat empty-state clicks as a north-star metric in isolation. A high click rate on a "Create sample data" button is only useful if those users go on to create real data later. Tie the empty-state event to the downstream activation event, not just the click.
Where we'd start
If you inherited a product and wanted a quick win, here's the order we'd tackle it in. Audit every empty state in the app and tag it as first-run, cleared, filtered, or error-adjacent — you'll find at least one miscategorised screen, probably several. Rewrite the copy on the top three most-viewed first-run empties using the domain-noun and outcome-verb rules above. Add a single shared EmptyState component with a required variant prop so no one can ship a generic one by accident. Wire up analytics for view, primary-click, and time-to-first-populated. Ship it, wait two weeks, look at the numbers.
That's usually enough to prove the point internally, and once the numbers move, empty states stop being the screen nobody owns.
Want a team like ours?
72Technologies builds production software for the kind of teams who actually read this blog.
Start a projectKeep reading
Form Errors Should Move Toward the User, Not Away: Inline Validation That Actually Helps
Most inline validation is punishment dressed up as UX. Here's how to design and build form errors that reduce abandonment instead of causing it.
Toast Notifications Are Broken: A Better Pattern for Async Feedback
Toasts get dismissed before anyone reads them, stack into unreadable columns, and vanish for screen reader users. Here's the pattern we use instead when work happens off the main thread.
Design Tokens That Survive Contact With Engineering
Most design token systems die the moment they hit a real codebase. Here's how we structure tokens so they actually get used — and stay in sync between Figma, Tailwind, and native.
