Empty States Are Your Best Onboarding Surface — Stop Wasting Them
Most empty states show a sad cloud and a 'No data yet' label. That's a dead pixel. Here's how to turn the zero state into the most persuasive screen in your product.

The first time a user opens your dashboard, they don't see your beautiful charts. They see nothing. That nothing — the empty state — is the single most under-engineered screen in most SaaS products, and it's quietly killing activation.
We've audited a lot of products in the last two years, and the pattern is depressingly consistent: teams spend weeks polishing the populated state, then ship a grey illustration and the words "No items yet" for the zero state. That screen is doing 80% of the persuasion work on day one. Let's fix it.
Why the empty state matters more than your landing page
By the time a user sees an empty state, they've already signed up. They've given you an email, maybe a credit card, definitely attention. They are leaning in. The landing page's job was to get them through the door. The empty state's job is to get them to the first valuable action — what product folks call the aha moment.
If the empty state says "No projects yet," you've handed the cognitive load back to the user. They have to guess what a project is, why they'd want one, and how to make one. Most won't bother. They'll close the tab and add your product to the mental pile of things they'll "check out later."
The three empty states people confuse
Not all empty states are the same. Treating them identically is the first mistake.
- First-use empty: the user has never had data here. This is an onboarding surface.
- User-cleared empty: the user archived, deleted, or filtered everything out. This is a confirmation surface.
- Error-adjacent empty: data failed to load or permissions are wrong. This is a recovery surface.
Each needs different copy, different actions, and a different emotional register. A reassuring "Inbox zero — nice work" is delightful after the user archives their last ticket. It's bewildering on day one when they've never had a ticket at all.
What a first-use empty state actually needs
For the first-use case — the high-leverage one — there are four jobs the screen has to do, in order:
- Name the value. Not the feature. "Track which deals are stalling" beats "Your pipeline is empty."
- Show the shape of success. A muted preview, a ghost row, a screenshot of what populated state looks like. Users need a mental model before they'll invest effort.
- Offer one obvious next action. Not three. One primary CTA, maybe one secondary escape hatch (import, sample data, watch a 30-second demo).
- Remove the blank-page tax. If you can pre-seed sample data they can poke at and then delete, do it. Friction at zero is a churn multiplier.
The "ghost row" pattern
One of the highest-ROI changes we've shipped for clients is replacing the centered illustration-and-button layout with a ghost row — a low-opacity version of what a populated row would look like, with a CTA inline.
It works because the user immediately understands the structure of the page. They see columns, they see what a record looks like, they see where the action lives. The abstract "add your first project" button gives way to a concrete "this is what you're building toward."
function ProjectsTable({ projects }: { projects: Project[] }) {
if (projects.length === 0) {
return (
<div className="rounded-lg border">
<TableHeader />
<div className="relative">
{/* Ghost row — visually present, semantically hidden */}
<div
aria-hidden="true"
className="opacity-30 pointer-events-none select-none"
>
<TableRow
name="Acme website redesign"
owner="Sam"
status="In progress"
updated="2 hours ago"
/>
</div>
<div className="absolute inset-0 flex items-center justify-center">
<div className="text-center max-w-sm">
<h3 className="font-medium">Track work that's actually moving</h3>
<p className="text-sm text-muted-foreground mt-1">
Projects let you group tasks, assign owners, and see what's stalled.
</p>
<div className="mt-4 flex gap-2 justify-center">
<Button onClick={createProject}>Create a project</Button>
<Button variant="ghost" onClick={loadSampleData}>
Use sample data
</Button>
</div>
</div>
</div>
</div>
</div>
);
}
return <PopulatedTable projects={projects} />;
}
A few things to notice. The ghost row is aria-hidden because it's decorative — screen readers should hit the heading and CTA directly, not a fake row. The sample data button is a graceful escape for users who aren't ready to commit. And the copy talks about outcomes ("what's stalled"), not features ("task management").
Copy rules that survive translation
Empty state copy is where designers and PMs fight, and the fight usually produces something committee-shaped. A few rules we lean on:
- No apologies. "Oops, nothing here yet!" infantilises the user. They know it's empty.
- No metaphors that don't localise. "Your inbox is squeaky clean" is charming in English and meaningless in seven other languages.
- Verb-first CTAs. "Create project" beats "Get started" because it tells the user what's about to happen.
- One sentence of value, one sentence of how. That's it. Three paragraphs of marketing copy on an empty state is a confession that your product isn't obvious.
When humour works and when it doesn't
Humour in empty states is a power move, and like most power moves, it's mostly wrong. It works when the empty state is a celebration (inbox zero, all tasks done) because the user's emotional state can absorb levity. It fails on first-use empties because the user is mildly anxious and trying to figure out if they made the right choice signing up. Comedy on top of anxiety reads as glib.
Instrumenting empty states
If you ship a new empty state and don't measure it, you've just done decoration. The minimum useful instrumentation:
- View event when the empty state renders, with a
variantproperty (first-use, cleared, error). - Click events on every CTA including secondary ones.
- Time-to-first-action: how long from empty-state-view to the user creating their first real record.
- Sample-data conversion: of users who loaded sample data, how many created something real within seven days.
In our experience, moving a first-use empty state from "illustration plus button" to "ghost row plus inline CTA plus sample data" tends to lift first-action rates noticeably — often in the range of 15–30% — though the absolute number depends heavily on how complex the underlying object is. A project is harder to create than a note.
Accessibility traps to avoid
Empty states are a common accessibility blind spot because they're often built as one-off components outside the design system.
- The empty state heading should be a real
<h2>or<h3>that fits the page's heading order, not styled<div>text. - Decorative illustrations and ghost rows need
aria-hidden="true"so screen readers skip them. - The primary CTA must be reachable by keyboard with a visible focus ring — yes, even when it's the only interactive element on the page.
- Don't rely on colour alone to signal that something is a placeholder. Low-opacity grey ghost rows can hit contrast issues; pair them with explicit copy or an
aria-labelon the container that says "No projects yet."
A checklist before you ship
Before an empty state goes to production, run it through this:
- Does it name a user outcome, not a feature?
- Is there exactly one primary action?
- Is there a low-commitment escape hatch (sample data, import, demo)?
- Does the user-cleared variant differ from the first-use variant?
- Is the heading a real semantic heading?
- Are decorative elements
aria-hidden? - Are you tracking view, click, and time-to-first-action?
If any of those are "no," you've got a dead pixel where your best onboarding surface should be.
Where we'd start
If you're staring at a product with twenty empty states and limited time, don't redesign them all. Pull the analytics, find the three empty states with the highest first-session view counts, and rebuild those first — almost always the main list view, the dashboard, and whatever the second-tab navigation lands on. Ship the ghost-row pattern, add sample data, write outcome-focused copy, and measure time-to-first-action for two weeks. That's usually where the activation lift hides, and it's a week of work, not a quarter.
If you want a hand auditing yours, our team does this kind of work on the product design and UX side regularly — the patterns are boring, but the impact rarely is.
Want a team like ours?
72Technologies builds production software for the kind of teams who actually read this blog.
Start a projectKeep reading

Skeleton Screens vs Spinners: When Each One Actually Wins
Skeleton screens aren't automatically better than spinners. Here's the decision tree we use on real projects, with code, timing thresholds, and the accessibility traps nobody talks about.

Focus Rings Are Not Optional: A Practical Guide to Visible Focus in 2026
Removing the focus ring is the most common accessibility regression we see in audits. Here's how to keep keyboard users happy without making your designer cry.

Toast Notifications Are Lying to Your Users
Toasts feel modern, but they're quietly failing the people who need them most. Here's how we audit, fix, or replace them — with patterns that hold up under accessibility and conversion scrutiny.
