Nobody designs the empty state, and it is the first thing users see
Design files show screens full of perfect content. Real users arrive at an app with nothing in it, on a bad connection, having just made a mistake. Those screens decide what they think of the product.
Open almost any design handoff and you will find beautifully composed screens with exactly the right amount of content. Four items in the list. Names of sensible length. Every avatar present.
No real user ever sees that screen. The first screen they see has nothing in it.
The states that decide the impression
Every screen that displays data has at least five states, and design usually covers one of them.
- Empty, because the user is new and has created nothing.
- Empty, because a filter or search matched nothing - a different message entirely.
- Loading, both first load and refresh, which should not look the same.
- Error, distinguishing no connection from something broke on our side.
- Too much - the list with 4,000 rows and the name that is forty characters long.
The first-run empty state is the most important screen in the product and is almost always an afterthought, usually a grey icon and the word Nothing here.
The two empty states are worth separating carefully, because they call for opposite responses. A new user with nothing needs encouragement and an action. Someone whose search returned nothing needs their query back, a way to widen it, and no encouragement at all. Showing the welcome message to somebody who has just filtered a list of four thousand records reads as the app not paying attention.
What an empty state should do
It has one job: get the user to the second screen. That means it should explain what goes here, why it is worth having, and offer the single action that creates the first item.
It should not apologise, and it should not be decorative at the expense of being useful. An illustration with no call to action is a dead end with better art direction.
An empty state is not a gap in the product. It is the product's first sentence.
Loading is two states, not one
First load and refresh look identical in most implementations and should not. On first load there is nothing to show, so a skeleton of the layout tells the user what is coming and makes the wait feel shorter. On refresh there is already content on screen, and replacing it with a spinner takes away something the user was reading in order to tell them that more is on the way.
The other half of this is the very fast case. A spinner that appears and disappears within a couple of hundred milliseconds reads as a flicker, and a screen full of them reads as instability. Holding the previous state briefly before showing any indicator removes an entire category of complaints about an app feeling janky.
Errors should say what to do
Something went wrong tells the user two things they already knew. A useful error names what failed, says whether it is likely to be their connection or our fault, and offers an action - retry, or a way out that does not lose their work.
The unforgivable version is an error that discards input. If a form submission fails, the form must still be filled in. We have seen this cost a client a measurable share of signups, and it is a ten-minute fix.
Distinguishing the two causes matters because they need different words. No connection is the user's situation and they can act on it, so say so and offer retry. A server failure is ours, and telling somebody to check their connection when their connection is fine sends them to reboot a router for no reason.
Design the overflow case on purpose
The too much state is the one most often discovered in production, because test data is always tidy and real data is not.
- A name of forty characters, because somebody's name is forty characters.
- A list with no natural end, where the design assumed a screenful.
- Text in a language that runs a third longer than English, which is most of them.
- A number with more digits than the column was drawn for.
Seeding a development database with deliberately awkward content costs an hour once and surfaces nearly all of it before anybody ships. It is more effective than any amount of reviewing, because these failures are invisible until the data is wrong in the right way.
How we scope it
Every component in our design systems ships with its states specified: default, loading, empty, error, and the overflow case. It adds perhaps fifteen percent to design time and removes the most common source of handoff disagreement, because the developer is no longer inventing behaviour from a single happy-path mockup.
The saving is not really in the design time. It is that a developer guessing at an empty state produces a placeholder, placeholders reach production, and the cheapest screen in the product ends up being the first one a new user judges it by.


