Empty state

Difficulty S

The state that isn't an error – a successful request with honestly nothing to show – and the screen most products never design because nobody asked for it.

Seen in: Near-universal – every list, inbox and dashboard has a zero moment; Linear and Notion treat it as a first-run stage, most products shrug

Projects0 of 0

No projects yet – that's the only setup left

Projects hold your team's briefs, files and decisions in one place. The first one takes about a minute to set up.

Same situation, but the screen does three jobs: says why it’s empty, sets the value of filling it, and points at exactly one primary action (hierarchy’s one-obvious-next-step rule, applied to a screen with nothing on it).

Teaches

  • Hierarchy: one obvious next step
  • The four states
  • Plain-language interface copy

What to notice, in order:

Empty is not an error. The request worked; there’s simply nothing yet. That distinction drives everything: an error apologises and offers a retry, an empty state orients – why is this empty, what is this space for, what’s the first move.

The considered version does exactly three jobs. Explain (what projects are), motivate (one minute to set up), direct (a single primary action). Note the hierarchy rule from the course showing up on a screen with almost nothing on it – one solid button, one quiet alternative.

The dead end isn’t broken, it’s mute. “No projects found.” is technically accurate and completely unhelpful – the reference example of an interface that answers the database’s question instead of the user’s.

This is one of the four states. Loading, success, error, empty: the habit (from the prompting-agents research) is asking for all four every time you brief a screen, because unprompted builds ship the success path only. The full matched set – one dataset rendered in all four states – arrives with the library build-out in Phase 4.

View the source – 75 lines of TypeScript/React, tokens only
import { useState } from 'react';
import '../demos/demos.css';

/*
  Specimen: empty state (library #12, S difficulty).
  The state that isn't an error: the request succeeded and there's honestly
  nothing here yet. The toggle contrasts the dead end most products ship
  with the considered version – explain why it's empty, point at exactly one
  next action. Original code (ADR #006).
*/

export default function EmptyState() {
  const [considered, setConsidered] = useState(true);

  return (
    <div>
      <div className="pc-controls" role="group" aria-label="Choose a version">
        <button
          type="button"
          className="chip"
          aria-pressed={!considered}
          onClick={() => setConsidered(false)}
        >
          The dead end
        </button>
        <button
          type="button"
          className="chip"
          aria-pressed={considered}
          onClick={() => setConsidered(true)}
        >
          The considered version
        </button>
      </div>

      <div className="es-panel">
        <div className="es-chrome">
          <span className="es-chrome-title">Projects</span>
          <span className="badge">0 of 0</span>
        </div>
        {considered ? (
          <div className="es-body">
            <div className="es-mark" aria-hidden="true">
              <span className="es-mark-diamond" />
            </div>
            <h4 className="es-title">No projects yet – that's the only setup left</h4>
            <p className="es-copy">
              Projects hold your team's briefs, files and decisions in one place. The first one
              takes about a minute to set up.
            </p>
            <div className="es-actions">
              <button type="button" className="btn btn-primary">
                Create your first project
              </button>
              <button type="button" className="btn btn-ghost">
                Import from another tool
              </button>
            </div>
          </div>
        ) : (
          <div className="es-body">
            <p className="es-copy es-dead">No projects found.</p>
          </div>
        )}
      </div>

      <p className="pc-note" aria-live="polite">
        {considered
          ? 'Same situation, but the screen does three jobs: says why it’s empty, sets the value of filling it, and points at exactly one primary action (hierarchy’s one-obvious-next-step rule, applied to a screen with nothing on it).'
          : 'A successful request with zero results – and the interface shrugs. Nothing explains what projects are, why there are none, or what to do about it. This is the default state of products built without asking for the empty state.'}
      </p>
    </div>
  );
}