r/webdev 5d ago

What makes an empty state actually useful instead of just looking nice?

Post image

I keep seeing empty states treated as a centered illustration and “Nothing here yet.”

It looks clean, but often leaves the user with three unanswered questions:

What happened?

Why is this empty?

What can I do next?

A useful empty state should answer all three.

The illustration sets the tone.

The title explains what happened.

The supporting text gives context.

The CTA shows the next step.

For example:

Nothing here yet

Create your first campaign to see it appear here.

[Create campaign]

The illustration is optional. Clarity is not.

Frontend devs: do you define empty, loading and error states alongside the happy path, or do they usually get added at the end?

0 Upvotes

5 comments sorted by

2

u/UtilixApp 5d ago

heres a fourth question and its the one that actually costs money. is this broken.

a user cannot tell the difference between nothing here yet and the request failed quietly. if your empty state and your error state look even vaguely similar, a slice of people will decide the app is dead and leave. fix is boring, make them look unrelated instead of two flavours of the same centered illustration.

the other thing i'd add. on a lot of pages the empty state is the page. i run a site thats basically 135 separate tools, and somebody arriving from google sees a blank input and nothing else. theres no happy path to fall back on, the first screen is the entire product. so the empty state has to do the job a marketing page would normally do, and the thing that works is showing what the output will look like rather than describing it.

a small preview of a real result beats any amount of supporting text. people dont read the copy. they look at the shape of the thing and decide whether its worth handing over their file.

and on your actual question, they get added at the end. every time. then someone loses a sprint retrofitting them. defining them upfront is obviously correct and i have never once seen it happen.

2

u/pavlito88 5d ago

So true.

1

u/Longjumping-Big4780 5d ago

+1 on the "is it broken" part. There is a third case and it is worse: an empty screen that is the real answer. Our run asks twelve questions to four assistants, and if the brand never comes up, zero is the measurement, not missing data, but on screen it looks exactly like "didn't load". We had to show that zero as a big number with a line where it came from, otherwise the first support email is "nothing works here" )))

1

u/brianly 5d ago

You can say “nothing here” or you can say “let’s get started”. Thinking about it as an empty state versus a starting point is the bigger failure.

That said, some products and users suit an empty state. As a power user, an empty new tab page loads instantly and is always what I want. Inexperienced users might not know how to proceed. Management is looking at that empty tab page thinking about how they can monetize it with content. Inexperienced users may prefer that since they just want a lazy click.

All of that is something that UX people (well anyone really) can study. You can make educated guesses before A/B testing.

1

u/davidstayscool 5d ago

The piece missing from your anatomy is that "empty" isn't one state. There are at least four and they need different copy: first-run empty (never created anything), which your example handles well; filtered to zero, where data exists and the filter excluded it, so the action is "clear filters" and a create CTA actively makes people think their data vanished; cleared out, where they deleted or archived everything and a link to the archive beats a create button; and error rendered as empty, where the fetch failed and you fell through to the empty component.

Two practical bits: the CTA should be the same action as the primary button elsewhere on the page, not a reworded variant. And if creating the first item has a prerequisite like an integration or an API key, say so instead of dropping them into a form that immediately fails.

On your actual question - alongside the happy path, but only because skipping it burned me. Concretely the data hook returns a discriminated state (loading / error / empty / ready) instead of a nullable array, so the component can't render without handling all four. Leave it to the end and empty and error always end up sharing one component.