Loading is not “No results”

No data yet means unknown, not empty. Show pending until it answers.

Guillermo Rauch@rauchg
Classic. It’s not “No results” it’s “we don’t have results yet, therefore we don’t know” aka should render a loading spinner or the previous screen should suspend until information is obtained. Instead, this flashes and confuses the user.
May 20, 2025View on X ↗

He was quoting a post about mistaking error states for empty states.

1 · Filter while it fetches

Click a status filter and watch both lists.

Empty while fetchingBad
False “No results”0
Keeps last resultsGood
False “No results”0
const { data, error, isPending, isFetching, refetch } = useQuery({
  queryKey: ["deployments", filter],
  queryFn: () => getDeployments(filter),
  placeholderData: keepPreviousData, // old rows stay while the new filter loads
});

if (isPending) return <ListSkeleton />;              // unknown: first load
if (error) return <ErrorState onRetry={refetch} />;   // failed is not empty
if (data.length === 0) return <Empty filter={filter} />; // the server said so
return <List rows={data} aria-busy={isFetching} dimmed={isFetching} />;

2 · Loading, empty and error

Pick what the server returns, then load orders.

Server returns
One state for allBad
Three statesGood

Why it matters, pros and cons

Why it matters

An empty state is a claim: there is nothing. Before the server answers you can't make it, so it flashes and misleads.

Pros

  • No flash of “No results” on every filter change.
  • Nobody acts on a false empty, like creating a duplicate.
  • Failures get a retry instead of a shrug.

Cons

  • Three states to design per list, not one.
  • Dimmed stale rows need a clear pending cue.
  • On very slow fetches, swap stale rows for a skeleton.