Hero image for Empty States as a Computable Contract for AI Design Agents

Empty States as a Computable Contract for AI Design Agents

What Is a Computable Empty State?

A computable empty state is a rule about a component that a program, including an AI design agent, can verify by reading the component’s code or markup rather than by judging how it looks. An empty state is what the interface shows when a region has no content to display: no search results, no first-run data, no data the user can access. The distinction matters because a blank region is the default, and the default is a poor one. One empty case is a search that finds nothing, which NN/g covers separately in its no-results guidance. The general point is: “The default of an empty space is to simply remain empty… ultimately creates confusion and decreases user confidence” (NN/g, Designing Empty States in Complex Applications).

For empty states, the computable question is narrower than “does it look right”. An agent can check whether a separate branch exists, whether text names the status, and whether structural noise was removed from the DOM. None of those checks needs a person to look at a screenshot.

Throughout, data-bound container means a component whose content comes from a data source, such as a list, table, grid, or chart, rather than from static markup. Screen reader means software that converts interface text into speech or Braille for blind users.

Why Empty States Break AI-Generated UI

The direct answer: empty states break AI-generated UI because an agent optimising for the populated view has no signal that the zero-data case exists, and the failure stays invisible until a user hits it. This is a hypothesis about agent behaviour, not a measured finding. See “What an Agent Cannot Yet Compute” below.

The user-facing failure is documented even though the agent-side cause is not. When a region is simply blank, users cannot tell whether the system is broken, slow, or empty: “Totally empty states cause confusion about how and whether the system is working” (NN/g). A related mistake is a “No records” message shown while data is still loading and replaced by content moments later, which teaches users to distrust the interface (NN/g).

Structural mistakes compound the problem. Carbon says an empty table should drop its column headers, because otherwise a screen reader has to traverse an empty grid before reaching the message (Carbon Design System, Empty states). An empty state that still renders toolbars and filters offers controls with nothing to act on: “Don’t include toolbars or filters in a full-page empty state because the user doesn’t have any items to search or filter” (PatternFly, Empty state).

Each of these is a property of the markup, which is why each can become a rule.

The Three Computable Rules

These rules are proposals to verify, derived from published guidance. They are not results from an evaluation we ran.

Rule 1: Every Data-Bound Container Has an Explicit Empty Branch

The rule. A data-bound container renders a distinct empty branch, separate from its loading and error branches.

What to check in code. Does the component contain separate conditionals for isEmpty, isLoading, and isError, or a single truthy check that renders the list or nothing? Pass means three branches. Fail means one v-if="loaded" that falls through to blank space when data is absent.

This maps to guidance that separates states by cause. NN/g distinguishes the completed-but-empty case from the in-progress case: “If there is no relevant data to display after a process has completed, use the empty space to provide a system-status message,” and “When a process is running, use progress indicators” (NN/g; see also NN/g, Progress Indicators). Carbon lists three basic empty-state types: “No data” (first use), “User action” (for example no search results, or a confirmation), and “Error management” (permissions, system issue, configuration required) (Carbon). PatternFly organises its guidance by situation, with usage pages covering Getting started, No results, Required configuration, No access, Back-end failure, Success, Creation, and Card, and size guidance by use case (PatternFly). The three-way loading, empty, and error split in the code is this post’s proposal, built on that guidance.

Pass example.

if (isLoading) return <Spinner />;
if (isError)   return <ErrorPanel message={error.message} />;
if (isEmpty)   return <EmptyState title="No results yet" />;
return <ResultsTable rows={rows} />;

Fail example.

if (loaded) return <ResultsTable rows={rows} />;
return null; // zero rows, no message, no error, no loading signal

Rule 2: The Empty Message States System Status in Text

The rule. The empty branch contains a text node that names the status (none exist, still loading, or failed), not only an illustration.

What to check in code and markup. Does the empty branch contain a text element with a status string? Pass means visible text such as “No results found. Adjust your filters and try again.” Fail means an illustration with no status string, or a “No records” message rendered during loading.

NN/g’s guidance is that a completed empty state should use a message that “briefly states that no content is available,” and that progress indicators carry the running case (NN/g). PatternFly makes the same point about wording, with headings that state the situation in plain language: “Unable to connect” as the do, against “Failure #121212: Connection error” as the don’t (PatternFly). Carbon treats empty-state illustrations as decorative, with empty alt text under W3C decorative-image guidance (Carbon). That means the status cannot live only in the picture. Error wording for the failed branch is covered in Carbon’s references, which point to NN/g, Error Message Guidelines.

Pass example.

<div class="empty-state">
  <h3>No results found</h3>
  <p>Adjust your filters and try again.</p>
  <img src="/art.svg" alt="" />
</div>

Fail example.

<div class="empty-state"><img src="/art.svg" /></div>
<!-- or: <p>No records</p> rendered while isLoading === true -->

The rule. In the empty branch of a table, the column headers and footer are absent from the DOM. The post’s recommendation is to remove them rather than visually hide them, so assistive technology never reads them.

What to check in markup. Does the empty branch render only the message container? Pass means no <thead>, no column headers, no pagination footer. Fail means an empty <thead> with zero rows, so a screen reader reads the empty grid before the message.

Carbon states this directly: an empty state “should replace the element that would ordinarily show,” and “column headers and footer should not be present,” a practice that “avoids having a screen reader read the entire table before getting to the message” (Carbon). PatternFly’s related rule extends the same idea to surrounding chrome: no toolbars or filters in a full-page empty state (PatternFly).

Pass example.

<div class="empty-branch" role="status">
  <h3>No results yet</h3>
  <p>Your search returned nothing.</p>
</div>

Fail example.

<table>
  <thead><tr><th>Name</th><th>Date</th><th>Status</th></tr></thead>
  <tbody><!-- zero rows --></tbody>
  <tfoot><!-- pagination --></tfoot>
</table>

How Each Guidance Source Covers the Rules

Each cell quotes or paraphrases the cited source, and “Not covered” means the source is silent. Material Design appears as historical context only. Google marks Material 2 as no longer maintained, so it is not treated as current guidance (Material Design, Empty states (M2)).

Source Rule 1: explicit empty branch Rule 2: status in text Rule 3: headers and footer removed
NN/g Covered: completed-empty and running cases are separated (“When a process is running, use progress indicators”) Covered: a status message “that briefly states that no content is available” Not covered
Carbon Partly: three types by situation (“No data”, “User action”, “Error management”); no loading branch Partly: illustrations are decorative with empty alt text, so the status sits in text Covered: “column headers and footer should not be present”
PatternFly Partly: usage variants by situation, from “Getting started” through “Creation” and “Card”; no loading branch Covered: headings state the situation (“Unable to connect” vs “Failure #121212: Connection error”) Partly: adjacent rule, no toolbars or filters in a full-page empty state
Material (M2, historical) Not covered Partly: “The most basic empty state consists of a non-interactive image and a text tagline” Not covered

What an Agent Cannot Yet Compute

No published source measures how design agents perform on empty states, so the three rules above are proposals to verify, not measured results. That gap is the honest boundary of this post.

Some empty-state quality sits outside what markup can check. Whether an empty screen should teach, prompt, or just state the facts is a judgement call. NN/g’s guidance depends on knowing whether a process has completed, which the application’s state machine answers, not the component’s markup (NN/g). Wording quality, per PatternFly’s do and don’t pairs, is something a human reviewer can judge, and a program can only approximate it (PatternFly). Illustration choice, tone, and whether a call to action earns its place stay design decisions. Carbon’s advice to use a tertiary button when several empty states share a screen is a rule a program could check, but whether the action is worth showing is not (Carbon).

So an agent can verify that a branch exists, that text carries the status, and that table headers are gone. It cannot yet verify that the empty state is the right one.

FAQ

What is the fastest way to check empty states in an existing codebase? Search data-bound containers for a single truthy conditional that renders rows or nothing, then confirm separate branches exist for empty, loading, and error. NN/g separates the completed-empty case, which takes a status message, from the running case, which takes a progress indicator (NN/g; NN/g, Progress Indicators).

Should an empty state keep the table headers so the layout does not jump? No. Carbon says the empty state should replace the element that would ordinarily show, and that column headers and footer should not be present, so a screen reader does not read the whole table before the message (Carbon). Keeping the layout stable is a separate problem, and a container with a fixed minimum height can address it without an empty grid. In our recommendation, that is the fix to try first.

Is an illustration ever sufficient on its own? No. Carbon treats empty-state illustrations as decorative, with empty alt text under W3C decorative-image guidance, so the status must be carried in real text (Carbon). PatternFly’s guidance also centres on a heading that states the situation in plain language (PatternFly).

How should the loading case differ from the empty case? The loading case shows progress. The completed-and-empty case shows a brief status message. Rendering “No records” during load and replacing it later is the failure NN/g describes as a misleading status message, and it breeds distrust of the interface. Keeping isLoading and isEmpty as separate branches prevents the swap; that split is this post’s recommendation.

The Bottom Line

Treat the empty state as part of the component’s contract, not as decoration. An agent or a reviewer can verify three things in code: a dedicated empty branch exists alongside loading and error, a text node names the status, and table headers and footer are absent from the DOM in that branch. Each check traces to published guidance from NN/g, Carbon, and PatternFly.

What the checks do not give you is a guarantee about agent performance, because no one has published that measurement. The rules are proposals until someone runs them against agent output.

How This Guide Was Built

This review is based on official design-system guidance and published usability research; we did not run these checks against a live product. It draws on four sources: NN/g’s empty-state article and its linked progress-indicator page (error-message wording comes via Carbon’s references); Carbon’s empty-states pattern; PatternFly’s empty-state design guidelines; and Material Design’s M2 empty-states page, cited only as historical because Google marks M2 as no longer maintained. Where a source is silent on a rule, the comparison table says “Not covered”. Quoted wording is copied from those pages, and no agent-behaviour claim is presented as measured fact.