# Empty states that don't look broken

A rule set for coding agents. Paste it into your `CLAUDE.md`, `AGENTS.md`, or `.cursorrules`.

Agents design for full data. Your users open the app and see zero data. The empty
state is the first screen every real user meets, and your agent has never met a
real user.

From krit.design. These came out of screens shipped into production repos.

---

## Paste this

```md
## Empty states

Every screen that renders data ships with a designed empty state, built in the
same pass as the populated state. Never as a follow-up.

1. Never render a zero as the headline. "$0" above a flat chart reads as failure,
   not as a new account. In the empty state, the metric is replaced, not zeroed.

2. Delete dead scaffolding. Empty chart axes, table headers with no rows, and
   skeleton chrome read as broken UI. Remove them from the empty state entirely.

3. Keep the outer frame. The card title, the date range control, and the
   surrounding navigation stay, so the user still knows where they are and that
   the screen finished loading.

4. One sentence, written as a fact about the future. Say what will appear here
   and name the exact condition that fills it.
   Good: "Revenue will appear here once your first customer is charged."
   Bad: "No data yet." / "Nothing here!" / "Get started with revenue tracking."

5. Exactly one action, and it must be the action that fills this panel. Not
   "Learn more." Not a primary and a secondary button. One.

6. No emoji. No stock illustration. No dashed-border upload box. If the state
   needs an object, use one brand-owned render at low contrast, under a quarter
   of the panel height.

7. Same padding, type scale, and border radius as the populated state. An empty
   state is not a special layout.

8. Distinguish empty from filtered from failed. "No customers yet" (empty),
   "No customers match these filters" plus a clear-filters action (filtered),
   and "Couldn't load customers" plus retry (error) are three different screens.

9. When you touch any existing screen that renders data, check its empty state
   before you finish. If it renders a zero, dead axes, a header-only table, a
   bare "No data", or nothing at all, fix it in the same change. Do not leave a
   screen you edited in a state these rules would reject.

Before finishing any data screen, render it with an empty dataset. If it looks
broken, unfinished, or like an error, it is not done.
```

---

## Run this once on your existing app

The rules above govern new work. This sweeps what you already shipped. Paste it
into your agent as a one-off message, not into `CLAUDE.md`.

```md
Audit every screen in this codebase for empty states.

1. Find every view that renders a collection, metric, chart, table, list, feed,
   search result, or detail panel populated from a data source.

2. For each one, work out what actually renders when that source returns
   nothing: an empty array, null, a zeroed metric, or no response at all. Read
   the component, do not guess from the file name.

3. Classify each screen:
   - designed: passes the empty-state rules
   - undesigned: renders a zero, dead axes, a header-only table, a bare
     "No data", or an unstyled string
   - missing: renders nothing, a blank region, or crashes

4. Check the filtered-empty path and the error path separately. A screen can
   have a designed empty state and still fall over when a filter matches
   nothing or the request fails.

5. Report one table, sorted with the most-visited screens first:
   file path | screen | what renders when empty | classification | the one
   sentence and one action you would use instead

Do not change any code in this pass. Report the table, then stop and wait.
```

When the table comes back, fix them highest-traffic first, one commit per
screen, so each is reviewable on its own.

Most teams find between eight and twenty screens in the undesigned or missing
columns on the first run. That is not a failure of your agent. Nobody prompted
for the zero-data case, so nobody got it.

---

## Why this one rule changes the whole app

Every dashboard, table, list, inbox, and chart in your product has an empty
state. An agent generates all of them from the same default, so one instruction
propagates across every screen at once. It is the highest leverage line you can
add to an agent instruction file.

## Checklist

- [ ] No zeroed metric standing in for the empty state
- [ ] No empty axes, no header-only tables, no ghost rows
- [ ] Card title and controls still present
- [ ] One sentence naming the condition that fills the panel
- [ ] Exactly one action, and it fills this panel
- [ ] No emoji, no stock illustration
- [ ] Padding and type scale match the populated state
- [ ] Empty, filtered, and error states are distinct
- [ ] Existing screens audited, not just the one you are building

## What it looks like

Three states of the same panel:

1. **Populated.** Recurring revenue, the chart, the customer table.
2. **What agents ship.** "$0" in gray, axes with nothing on them, a table header
   with no rows. Reads as broken.
3. **Designed.** No zero, no dead axes. The card title and date range stay. One
   crafted object, one sentence naming the condition, one action.

---

Written by Jaideep Singh, who redesigns real screens and ships them as a pull
request into your repo. Two weeks, $3,000. krit.design
