Stands in for a list or panel that has nothing to show, and says in plain words what is empty, why, and what to try next.
When to use
Use EmptyState in place of a list, a search or a panel that has nothing to show, to say what is empty, why, and what to try next.
| Use instead | When |
|---|---|
| DataStatePanel | A data view's message follows its data: while it loads, when there is none, or when it could not load |
| Alert | The message sits beside the content rather than in its place |
Kongens Nytorv
Metro M1, M2 and M3.
No matches for Nørrebro st
Props
| Name | Type | Default | Description |
|---|---|---|---|
title* | ReactNode | — | What is empty, in a few words: "No matches for Nørrebro st". Required. Replaces the HTML title attribute |
children | ReactNode | — | The sentence under the title: why it is empty and what to try. "Try a line number such as 5C." |
icon | ReactNode | — | An icon from Perron's set above the title, for the eye only: it is hidden from screen readers. It renders at 16 px, icon.size.400; for an icon drawn for another step, set --perron-icon-size to that step's icon.size.* on the icon part through slotProps.icon |
action | ReactNode | — | What the person can do about it, such as a button that clears the search, below the sentence |
slotProps | { icon?: ComponentProps<"div", {}, HTMLProps> | undefined; title?: ComponentProps<"p", {}, HTMLProps> | undefined; description?: ComponentProps<"div", {}, HTMLProps> | undefined; action?: ComponentProps<"div", {}, HTMLProps> | undefined; } | — | Props for each part: icon, title, description, action. |
title (required)ReactNode, default —- What is empty, in a few words: "No matches for Nørrebro st". Required. Replaces the HTML
titleattribute childrenReactNode, default —- The sentence under the title: why it is empty and what to try. "Try a line number such as 5C."
iconReactNode, default —- An icon from Perron's set above the title, for the eye only: it is hidden from screen readers. It renders at 16 px,
icon.size.400; for an icon drawn for another step, set--perron-icon-sizeto that step'sicon.size.*on the icon part throughslotProps.icon actionReactNode, default —- What the person can do about it, such as a button that clears the search, below the sentence
slotProps{ icon?: ComponentProps<"div", {}, HTMLProps> | undefined; title?: ComponentProps<"p", {}, HTMLProps> | undefined; description?: ComponentProps<"div", {}, HTMLProps> | undefined; action?: ComponentProps<"div", {}, HTMLProps> | undefined; }, default —- Props for each part:
icon,title,description,action.
Use it
import { EmptyState } from '@mylder/perron'Ask your agent
search_docs({ query: "EmptyState" })Keyboard and ARIA
EmptyState has no keys of its own and is never focusable. The caller's action is reached in reading order.
| Key | Does | From |
|---|---|---|
Tab | Moves to the first focusable element in the action, after the sentence | The browser |
Shift+Tab | Moves back out of the action, to whatever came before the EmptyState | The browser |
| Element | Role | Accessible name from | States announced |
|---|---|---|---|
root | None (generic div) | None | None |
icon | Hidden: aria-hidden="true" | None | None |
title | Paragraph, or a heading when slotProps.title.render makes it one | Its text, from title | None |
description | None (generic div) | None | None |
action | None (generic div); the caller's controls carry their own roles | The caller's controls' own names | Theirs |
- Decided: the root carries no live-region role, leaving the caller to add
role="status"when the EmptyState replaces results while the person types or filters. An empty state shown with the page on load needs no announcement, the accessibility rules ask for live regions on changes of state only, and a search field such as SearchField announces its own empty results.
What axe won't catch, from the contract, each with how this component meets it:
- Everything the eye is told, the ear is told, for every shape each prop accepts: the icon is
aria-hiddenand decorative, and the title, read as text, says what the icon shows. - Every landmark and named region has an accessible name: none here; the root is not a landmark or region.
- Every default label can be overridden: none here; EmptyState owns no string.
- Every shape the type accepts renders, in every variant:
title,children,iconandactioneach render a string, an element and an array;descriptionandactionaredivs, so block content such as a list is valid inside them, and the title takes phrasing content as apor a heading does. - A hidden copy kept for animation is
aria-hidden, and a collapsed region isinert: none here; nothing animates or collapses. - Native semantics first: a
pfor the title, made a nativeh2toh6throughrenderwhen it heads a section; the action holds the caller's ownbuttonora href. - Focus moves in reading order and returns on close: the action follows the sentence in the DOM and on screen; EmptyState never moves focus when it appears, and nothing in it closes.
- Every target is at least
size.target-minsquare: EmptyState has no target of its own; the action's targets are the caller's components, which keep their own sizes. - Loops stop when the system asks for less motion: none here; nothing moves.
Owned strings
No owned strings: every word EmptyState shows comes from the caller, through title, children and action, and the icon is hidden from screen readers.
Do and don't
For a caller using the component, with the reason. Each row is a rule: a must row's Don't is never right, a should row's needs a reason, and Checked by says what catches the Don't in a caller's code.
| Rule | Level | Do | Don't | Checked by |
|---|---|---|---|---|
| DD-1 | must | Say what is empty, then what to try, in plain sentences: "No matches for Nørrebro st" over "Try a line number such as 5C." | Write "Oops! Nothing here!": content design has no exclamation marks, and the title should name what is missing | review |
| DD-2 | must | Say only what the data knows: "No departures in the next hour" when the timetable covers an hour | Write "No departures" when there may be some later: the brand never claims more certainty than the data has | review |
| DD-3 | must | Use it for a list, a search or a panel with nothing in it | Use it for loading or for missing real-time data: loading says "Loading", and missing data uses the data-state words such as "No signal" | review |
| DD-4 | must | Give one neutral action at most, such as clearing the search or the filters | Add a brand action when the panel already has one: a screen has one brand action at most | review |
| DD-5 | must | Pick a decorative icon from Perron's set that matches the title, or none; it renders at 16 px, and one drawn for another step takes that step's icon.size.* through slotProps.icon | Let the icon carry meaning alone, or use an emoji: the icon is hidden from screen readers and Mylder uses no emoji | review |
| DD-6 | must | Make the title a heading through slotProps.title.render when the EmptyState stands in for a whole section | Wrap the title in your own heading element inside title: the part is a p until render swaps it, and a heading inside a p is invalid | review |
| DD-7 | must | Add role="status" on the root when it replaces results as the person types or filters, so the change is announced | Add a live region to an EmptyState shown on load: there is no change to announce, and it adds noise | review |