Dev.to WebDev πŸ›  Dev πŸ‘ 0 πŸ“– 3 min read

The empty states in my app blamed the user, so I wrote a linter for that

I shipped a SaaS last year and, like everyone, spent weeks on the happy path. The settings screen looked great when data existed. The first time a real user hit it with zero entries, they saw: "No data found." That sente

I shipped a SaaS last year and, like everyone, spent weeks on the happy path. The settings screen looked great when data existed. The first time a real user hit it with zero entries, they saw: "No data found." That sentence implies they did something wrong. They hadn't. That was the moment I cared about empty and error states as a real surface, not an afterthought.

EmptyKit came out of fixing that pattern across three products. You paste a screen's context β€” what it shows, what the user was trying to do β€” and it returns empty-state and error-state copy with a no-blame / no-apology check and a length lint. The point isn't clever writing. It's removing the reflexive "Oops!" and "You forgot to…" that quietly punishes users for our missing data.

Why empty states get neglected

They sit at the bottom of the build order. Designers mock the filled card; engineers hardcode a truthy check; product never writes the words. So the copy defaults to whatever the framework printed, usually "No results" or a stack of raw nulls.

The cost is real but quiet. A blank screen with a cold message is where trial users decide the app is broken. I've watched session recordings where someone bounced at exactly that moment. The fix is a sentence, but only if someone writes it on purpose.

What the no-blame lint actually flags

The tool is conservative. It flags first- and second-person blame ("you", "your fault"), apology openers ("sorry", "oops"), and copy over a length budget that depends on the state type. Empty states get more room than error toasts. It also checks for a next action: an empty state without a path forward is just a dead end with nicer words.

It does not invent tone. You pick a preset β€” neutral, warm, playful β€” and it stays in lane. The output is a draft, not a verdict. I still read every line before it ships because the context you paste determines whether "warm" reads as friendly or as noise.

Where this fits in a build

I keep EmptyKit in the same tab as the component work. When a view reaches the "handle the empty case" step, I paste the context and get a starting line, then trim. It's paste-first by design β€” no account, no project setup, just the screen and the words.

The honest limit: this is a writing aid, not a UX audit. It won't tell you whether an empty state should exist at all, or whether the better fix is to pre-populate the screen. Those are product calls. What it removes is the excuse that nobody had time to write the sentence.

What I'd still do by hand

The lint catches blame and apology, but it can't judge whether an empty state should exist at all. I've had screens where the right fix was to pre-fill sample data so the user never saw emptiness, and no copy tool would suggest that. When I'm unsure, I'll open EmptyKit for the words, then step back and ask whether the state is even the right design. The two questions are separate, and mixing them up is how teams ship polite dead ends. Copy is the cheapest layer to fix, so I fix it first and revisit the structure later if the bounce rate says the screen still fails.

FAQ

What does EmptyKit actually produce?

It returns empty-state and error-state microcopy for a screen you describe, plus a no-blame lint and a length check. You get draft lines in your chosen tone, not a finished, reviewed copy block.

Is this a replacement for a UX writer?

No. It's a first pass that removes blame and apology patterns and keeps copy short. A human still decides whether the message fits the product and the moment.

Does it need my design files?

No. You paste the screen context in plain text β€” what it shows and what the user wanted. The tool works from that description and does not store what you paste.

Can I use it for error messages too?

Yes. Error states get a tighter length budget than empty states, and the lint still flags blame and apology openers the same way.

πŸ“° Read the original article on Dev.to WebDev

Originally published by Dev.to WebDev. Aggregated on AIWithGhost for educational purposes β€” full credit and traffic to the original publisher.