empty states
Sometimes a list has nothing to show. That can be good news, because everything is done. It can be a first visit, before anything was added. Or it can be a problem: the list couldn't load. An empty state says which one it is, and what people can do next, so they never have to guess.
The empty state component draws all of them; its page shows it live, in every kind and size.
when to show one
Show an empty state in the place of the content: in a table's body, in a worklist under its filters, or as the whole page, when a list, a table or a page has nothing to show and saying why helps.
Other things look empty for a moment, but need something else:
- Content that is still loading. Show the loading state instead; see loading and progress. An empty state before the content has loaded claims something nobody knows yet.
- A problem with content that is still on the screen, such as a refresh that failed. That is a banner above the content; see message handling.
- A confirmation, like “8 episodes queued”. That is a toast.
- One empty value in a row. The cell says it: a date cell shows a dash, read out as “no date”.
- A short table nobody acts on. Its own one-line empty row is enough.
anatomy
- The place. Where the content would be. The empty state takes the content's place, not the page's: the title, the tabs and the filters stay, so people can change what made the list empty.
- The picture. A simple line drawing for each kind. It is decoration; the words carry the meaning.
- The title. What is true, in a few words. It is a real heading, so people can find it.
- The description (optional). Why, and what to do, in a sentence or two.
- The action (optional). At most one emphasized button, for the next step, and one quiet one beside it.
the five kinds
| kind | it says | its action |
|---|---|---|
| Empty | Nothing is here yet | The step that adds the first item |
| Done | Nothing is left to do: good news | Usually none |
| No match | The search or the filters match nothing | “clear filters” |
| Error | The content couldn't be loaded | “try again” |
| Unknown | It can't be told yet | The step that finds out |
which kind
Pick the kind by what is true, not by what the screen looks like. A list that failed to load, or can't be checked yet, isn't done.
| situation | kind | title | action |
|---|---|---|---|
| Nothing has been added yet: a new list, a first visit | Empty | “nothing tracked yet” | The step that adds the first item, emphasized: “import a list” |
| Items exist, but none is due yet, like a “not yet aired” tab | Empty | “132 not yet aired” | None |
| The work is done: nothing is left to act on | Done | “nothing missing” | None |
| One tab or filter of the list is done | Done | “nothing needs attention” | None |
| The search or the filters match nothing, but the list has items | No match | “no matches” | “clear filters” |
| The list couldn't load, and there is nothing older to show | Error | “couldn't load the list” | “try again” |
| It can't be told yet: a check hasn't run, or what the library holds is unknown | Unknown | “nothing can be checked yet” | The step that finds out, emphasized: “ask the catalog again” |
| The list is still loading | No empty state | — | The loading state |
| A refresh or an action failed, and the content is still there | No empty state | — | A banner above it, with a way forward |
when it couldn't load: say why
“Couldn't load” covers several different problems, and each one has a different way out. Say what went wrong, as far as the app can tell, and offer the one thing that can help. Trying again can't bring back something that was removed, so that screen offers the way back instead.
| what went wrong | title | description | action |
|---|---|---|---|
| No answer: the connection dropped or the server didn't reply | “couldn't load the library” | “Can't reach the server.” | “try again” |
| The server ran into a problem | “couldn't load the library” | “The server ran into a problem.” | “try again” |
| The server is busy | “couldn't load the library” | “The server is busy.” | “try again” |
| The server didn't accept the request | “couldn't load the library” | “The server didn't accept the request. Check for an app update.” | “try again” |
| It was removed | “this title isn't in the library anymore” | None | “back” |
| People don't have access to it | “you don't have access to this” | None | “back” |
| People are signed out | No empty state: the app signs them in again, or takes them to sign-in and back to where they were | — | — |
The title names what couldn't load, in the screen's own words: “couldn't load this title”, “couldn't load your watchlists”. The description gives the reason and nothing the button already says: “try again” is the button, so the sentence above it never asks people to try again. Only what no button offers, such as an app update, is said in words. The way out gets the focus first. The code that tells the cases apart goes under details, for the people who fix things.
where it goes
- In a table: in its body, under the header row, across every column, at the small size.
- In a worklist: in place of the table, at the usual size, under the tabs and the filters. Leave the selection toolbar out while the list is empty: there is nothing to select.
- As the page: at the large size, when the whole page has nothing to show.
- The title's heading level follows the page around it, so it fits the page's outline.
what happens next
- The action works in place. Pressing “clear filters” clears them, and the list comes back. Pressing “try again” loads the list again, and shows the loading state while it runs. The unknown kind's action starts what finds out, and says so in a toast: “asking the catalog again — the list follows”.
- The empty state goes as soon as there is something to show.
- The filters stay while a search matches nothing, so people can change one word instead of starting over.
- An empty state only shows once the content is known.
keyboard and screen readers
- The title is a real heading, so people can find the empty state by its heading. Set its level so that it fits the page.
- The picture is decoration: the title and the description carry the meaning, never the picture or its colour.
- The empty state isn't read out when it appears. Say the change where people are: the filter bar's count is read out, so “0 of 12” is heard as the search empties the list.
- The action is a button where the empty state sits, in the usual order of the Tab key.
Today:
- Pressing the action usually removes the empty state, button and all, so nothing has the focus afterwards. After “clear filters”, move the focus yourself, to the search field for instance.
- The empty state doesn't hold you to one emphasized action: it shows whatever buttons it is given.
the words
- Title. What is true, in the list's own words, in a few of them: “nothing missing”, “no matches”, “couldn't load the list”, “nothing can be checked yet”, “nothing tracked yet”. Not “no data”, “empty”, “oops” or “error”.
- Description. Why, and what to do, in one or two sentences, in sentence case: “Every aired episode is in the library.”, “Nothing in this list fits the search and filters.”
- Action. A verb and what it acts on: “clear filters”, “try again”, “import a list”, “ask the catalog again”.
- Counts, when they help: “132 not yet aired”.
- No technical detail in the title or the description: no codes, ids or raw errors. When people need them, they go under a banner's details.
- Good news is plain. “nothing needs attention”: no celebration, and no apology.
- No blame. “nothing tracked yet”, not “you haven't added anything”.
related
- Empty state: the component, live, in every kind and size.
- Message handling: when a problem is a banner or a toast, not an empty state.
- Data table: its empty body and its loading state.
- Worklist: where most empty states live.
- Meaningful text: why each case says only what is true for it.
specs
- Which case. Decided by the HTTP status, never by the error message: no answer or 408 is the connection case, 5xx the server, 429 busy, 404 and 410 removed, 403 no access, 401 signed out, and any other 4xx not accepted.
- 404 on a list. A list can't be removed. On a list screen, 404 and 410 mean the app and the server don't match, so they are the not-accepted case, with its hint to check for an app update. “Removed” is only for a page about one thing, such as a title or a person.
- Signed out. The app first tries to renew the sign-in silently; when that can't reach the sign-in service, it is the connection case. Only when the sign-in is refused does the app take people to sign-in, and back to where they were afterwards.
- Details. The status and its reason phrase, such as “HTTP 503 Service Unavailable”, go under details, never into the title or the description.