A blank list screen in Git Dojo's early build looked broken, not empty
Every RAXXO tool now ships with a written empty-state script before the first line of UI code
The rule is three parts: say why it's empty, show one action, never show a bare rectangle
Statusline Builder's zero-state now converts better than its populated dashboard because it explains instead of waits
The Screen Nobody Designs Until It's Too Late
Every product screen has a best day and a worst day. The best day is full: a dashboard with data, a list with rows, a history with entries. The worst day is empty, and it's usually the very first day a real person sees the tool, because a brand new account has nothing in it yet. I used to design for the best day and let the worst day happen by accident.
The moment this caught up with me was an early build of Git Dojo. The lesson list screen looked great once a learner had started a few exercises, progress bars filling in, streaks counting up. But the first time anyone opened it, before touching a single lesson, the screen was a header and then nothing. No text, no button, just a gray rectangle where content would eventually go. I only noticed because a friend testing the build sent me a screenshot with one line: "is this broken?"
It wasn't broken. It was empty. But a person can't tell the difference between "nothing here yet" and "something failed to load" unless the screen tells them which one it is. That's the whole job of an empty state: it's the only screen whose entire purpose is to answer a single question the moment someone lands on it, and if it doesn't answer that question, silence reads as a bug.
I'd already learned a version of this lesson with loading states, where a shaped placeholder beats a spinner because it tells you what's coming. Empty states are the same problem from a different angle. A loading state says "this is on its way." An empty state has to say "this is supposed to look like this, and here's what to do next." Skip that second half and you've built a screen that technically renders and functionally confuses.
What made it worse in Git Dojo's case specifically is that the tool teaches git to people who are already nervous about doing something wrong in a terminal. An unexplained blank screen in a beginner tool doesn't just look unfinished, it actively adds to the exact anxiety the product is trying to remove. That's when I stopped treating empty states as an edge case and started treating them as a screen I write before I build anything else.
Three Tools, Three Ways I Got It Wrong First
Git Dojo's blank lesson list was the first case, but it wasn't the only one, and each tool taught me a different flavor of the same mistake.
OhNine is a menu bar app that tracks Claude usage limits. Its first-run state, before it's connected to anything, used to just show a dash where the usage number belonged. A dash is ambiguous. Is that zero usage, a connection that hasn't happened yet, or a fetch that failed silently in the background? I had built a state that looked identical whether everything was fine or everything was broken, which is close to the worst thing an empty state can do, similar to how the onboarding screen I rewrote three times for OhNine kept failing until it told people what was actually happening. I replaced the dash with one line of text: "not connected yet" plus the single button to connect. Now the zero state and the broken state look nothing alike, on purpose.
Statusline Builder had a subtler version of the same issue. Its editor opens to a live preview panel, and a brand new project has no statusline configured, so the preview panel had nothing to preview. My first pass just left the panel visually empty with the editor controls active above it, which meant someone could sit there adjusting settings for a statusline that displayed nothing, with no clue why. The fix was to seed every new project with a minimal starter statusline instead of leaving the canvas blank. It's a different kind of empty-state fix: instead of explaining the emptiness, I removed it by giving people a real starting point instead of a void.
That change turned out to matter more than I expected. Once the preview panel always showed something, even a plain starter line, more new visitors stuck around long enough to try changing a single setting and see the preview react. A populated dashboard sells the finished idea, but a working starter statusline sells the moment someone realizes they can shape it themselves, and that moment is what actually keeps people in the editor. I didn't plan for the empty state to outperform the full one at getting someone to their first edit. It just did, once it stopped being empty in the way that mattered.
The third case was smaller but taught me the most. A collection page on the store itself, filtered down to zero results because of a typo in a query parameter, showed nothing but a category header and white space. No products, no message, no way back to the full catalog. That one wasn't even a first-run problem, it was a dead end reachable from a normal browsing path, and it's the case that convinced me empty states aren't just an onboarding concern. Any screen that can legitimately show zero items needs its own zero-item design, whether that happens on day one or three clicks into a normal session.
The Rule I Actually Follow Now
After those three, I stopped treating empty states as something to notice in testing and started writing them as a required part of any new screen, before the screen has real content behavior at all. The rule has three parts, and I check all three before I consider a screen finished.
First, say why it's empty. Not a generic placeholder, the actual reason: no lessons started yet, no statusline configured yet, not connected yet, no results for this filter. The reason changes what the person should do next, so a generic "nothing here" line is barely better than nothing at all.
Second, show exactly one action, not a menu of possibilities. The empty state is not the place to list every feature the tool has. It's the place to point at the single next step that gets someone out of the empty state. In Git Dojo that's "start lesson one." In OhNine that's "connect account." A screen that offers five options where it should offer one is asking a confused person to make a decision, which is the opposite of what an empty state should do.
Third, never ship a bare rectangle, ever, no matter how minor the screen feels. This is the rule that costs the least effort and gets skipped the most, because an empty state often shows up on a screen that otherwise works fine, so it's tempting to treat it as good enough. It isn't. A visibly designed empty screen, even a simple one, tells someone the product is finished. A blank one tells them to wonder.
There's a fourth thing I check now that isn't strictly one of the three, but sits underneath all of them: whether the emptiness is permanent or temporary, because those need different treatment. A brand new account is temporarily empty, it fills up the moment someone acts, so the empty state can point straight at that first action. A filtered search with zero matches is a different shape of empty, the person already acted and got nothing back, so the message has to acknowledge the filter and offer a way to loosen or clear it rather than just repeating a generic "get started" prompt that doesn't fit what actually happened. Treating both cases with the same copy is how the store's zero-result collection page ended up as dead space instead of a helpful detour back to the catalog.
I keep this as a literal checklist item alongside the accessibility pass every section gets before it ships, because both are the kind of thing that's invisible when done right and glaring when skipped. Neither one shows up if you only test the happy path with a populated account.
How I Actually Test For It
The only reliable way I've found to catch a bad empty state is to stop testing with my own accounts. My accounts are never empty, because I'm the one who filled them in while building the tool. Every empty state I've shipped wrong made it past me first and only got caught by someone opening the tool cold.
So now, before any tool ships, I create a genuinely fresh account and walk through the exact first five minutes a stranger would have, on a phone first, since that's also where I test every other part of a RAXXO tool before I trust the desktop version. I'm specifically looking for any screen that can show zero of something: zero lessons done, zero projects saved, zero results returned, zero history logged. I try to reach each one by the shortest path a real user would take, not the path I know from building the thing.
The beta group I text before any tool ships helps here too, because they're the closest thing to that stranger's first five minutes that I get before a wider release. More than one empty-state fix in this piece came from a beta tester screenshotting a screen and asking, in effect, the same question my friend asked about Git Dojo: is this broken? That question, asked honestly by someone who has no reason to be diplomatic about it, is the best empty-state test there is. If I get it even once, the screen isn't done yet.
Bottom Line
An empty state is easy to skip because it never shows up while you're building the feature it belongs to, you're too busy filling the screen with real data to notice what it looks like without any. But it's often the very first screen a new person actually sees, and if it looks broken instead of intentional, that's the impression the whole tool starts from.
The fix isn't complicated. Say why the screen is empty, give one clear next step, and never leave a blank rectangle where a design should be. What took real repetition was remembering to apply that rule before a screen ships, not after a tester flags it. Git Dojo's blank lesson list is what taught me that lesson the hard way. Every tool since has had its empty states written down before its full states were finished, and every one of them has been better for it.
Top comments (0)