Stop Using Empty States for Loading and Error States

July 11, 2026 Rens Jaspers UI UX Frontend

Years ago I wrote about this already:

Stop Pretending The Error Never Happened! Be Explicit About Loading State

It still happens everywhere.

An empty state should only be shown when you successfully loaded the data and the result is actually empty.

Do not use it while data is still loading. Do not use it when loading failed.

I was reminded of this recently while looking at the LiteLLM admin panel. It showed that all of my API keys had disappeared.

But they hadn't at all.

My VPN was disconnected when I opened the page. The SPA itself was already cached, but every XHR request returned 403 Forbidden. The UI never displayed an error. Instead, it simply showed 0 API keys. The logs page behaved the same way: no loading indicator, no error, just 0 logs.

Because the UI claimed everything had loaded successfully, I never considered checking the Network tab in DevTools. I assumed the data itself was gone and unnecessarily interrupted colleagues to investigate.

From my background in medical software, I probably care more than average about data integrity. Still, this should be a basic UI principle for every application, whether it is a banking app, an admin dashboard or a casual game.

The solution is simple.

Nothing has loaded yet? Never show "0 items". Show a loading skeleton, spinner or "Loading…". Showing "0 items" tells the user that loading completed successfully and the result is empty. That is simply incorrect. Showing stale data is fine, as long as it is clear that a refresh is in progress.

Loading failed? Tell the user. Error states are important information. In my case it resulted in an unnecessary interruption for a colleague. In a financial or medical dashboard the consequences could be much more serious. A UI should never communicate "0 critical alerts" when the real situation is "we failed to load the alerts."

One final recommendation: centralize this logic.

Wrap your loading, success, empty and error state rendering in a reusable presentation component. Every screen should use the same implementation. You only have to test it once, your UX stays consistent, and developers are much less likely to accidentally hide failures behind an empty state.