Components · Feedback & status

Empty state

Nothing on screen is the most ambiguous thing an interface can show. In clinical software it has at least four causes, three of which are problems — so an empty state's job is to say which.

Stable · v1.0 shadcn/ui WCAG 2.2 AA

Overview

In consumer software an empty state is a chance to be charming. Here it is a diagnostic surface, because "no cases awaiting review" and "the ECG feed died an hour ago" produce identical screens unless the component distinguishes them — and a clinician reading the first when the second is true will stop looking.

The rule underneath everything on this page: an empty state must prove why it is empty. A timestamp and a source count do more for safety than any illustration.

Diagnostic

Names its cause. Genuinely empty, filtered, still loading and failed are four different states and never share a rendering.

Evidenced

A healthy empty state carries proof of life — when the system last checked, and how many sources it is watching.

Plain

No illustrations, no encouragement, no personality. A clinician deciding whether to trust an empty queue needs facts, not reassurance.

The four empty states

CauseSeverityMust say
Genuinely emptyInformational The state, plus proof of life — last check, sources monitored
FilteredAdvisory How many rows exist, what is hiding them, and a one-action reset
Loading— Skeleton rows. Never a blank region, never a message claiming emptiness
FailedUrgent That data could not be loaded, which sources are affected, and what to do meanwhile
Failure is never an empty state

If the system could not load the data, the region does not render an empty state at all — it renders an Alert Banner at Urgent. Empty means "we looked and there is nothing". Failed means "we do not know". Collapsing the second into the first is the single most dangerous thing this component can do, because it converts a system fault into apparent clinical good news.

Anatomy

Genuinely empty · with proof of life
No cases awaiting review
The queue is current. New ECGs appear here automatically.
last analysis 14:47:12 · monitoring 4 of 4 sources · updated 8 s ago
ElementRule
Title States the fact, not a feeling. "No cases awaiting review", never "All caught up".
Description What will change this state, in one sentence.
Proof of life Required on any clinical queue. Last successful check, sources healthy versus total, and freshness of the view.
Action Only where one exists — clearing a filter, retrying. Never an invented one.

The other three

Filtered · loading · failed
No cases match the current filter
23 cases in scope, all hidden by filter: my patients only. Critical cases are always shown regardless of filter.
Patient Finding Waiting

Note that only the first is an empty state at all. Filtered renders an Advisory, failed renders an Urgent, and loading renders skeleton rows that reserve the same space the real rows will occupy — see Grid & layout.

Writing it

Do
No cases awaiting review
The queue is current.
last analysis 14:47:12 · monitoring 4 of 4 sources

Empty and demonstrably alive. A clinician can tell the difference between quiet and broken without leaving the screen.

Don't
All caught up!
Nothing to review right now.

Congratulates the clinician on a state that may be a dead ingestion pipeline. Identical rendering whether the ward is quiet or the feed died an hour ago.

Freshness

An empty state is a claim about now, and it decays. Three rules:

What the component enforces, and what it only asks for

One required prop carries the entire argument of this page.

RuleHeld by How
An empty region says what was searchedThe component searched is required, so a region cannot claim there is nothing without saying where it looked.
Asserted: EmptyState: proof of search is rendered
The four causes are not conflatedYou Nothing found, filtered to nothing, still loading and failed are four different states. The component renders the title and description you choose; picking the wrong one turns a failure into an absence.
A retry exists where the cause is a failureYou action is whatever you pass, including nothing.
The scope described is the scope queriedYou “this site, all dates” is a string. If the query was narrower, the reader has been told something false in the most reassuring possible way.
Why this is written down

A rule stated on a component page reads as a rule the component keeps. 1 of these 4 are; 3 are obligations this page was quietly handing to the reader. This component makes it impossible to say nothing at all. It cannot make what you say accurate, and an inaccurate scope statement is worse than none.

Do's and don'ts

Do
No cases awaiting review
The queue is current.
last analysis 14:47:12 · monitoring 4 of 4 sources

Empty and demonstrably alive. Quiet ward and dead pipeline are distinguishable without leaving the screen.

Don't
All caught up
Nothing to review right now.

No timestamp, no source count. Identical whether the ward is quiet or the ECG feed died an hour ago.

Do

No prior studies

Searched this site, all dates · other sites not included

The scope is stated, so the reader knows what “none” is none of.

Don't

No prior studies

Searched all records

The query ran against this site only. The reader has been told something false in the most reassuring possible words, and will not order the outside films.

Accessibility

Outcomes of use

What this contributes to, in the terms of Usability & context of use. These are attributes believed to contribute to an outcome; the outcome itself is settled by observing real use in a specified context, not by this page.

Clinical safety notes

Risk controls this page specifies

Trace these in your risk file (ISO 14971) and usability engineering file (IEC 62366-1). Which of these the component keeps and which are yours is stated in the guarantees table; a control the component does not keep is one your risk file must show you keep.

Implementation

$ npx shadcn@latest add https://md.notjustany.tech/r/empty-state.json
<EmptyState
  title= "No prior studies for this patient" 
  searched= "this site, all dates"    // required: what was looked through
  description= "Studies from other sites are not included in this search." 
  action={<Button variant= "outline" >Search other sites</Button>}
/>

// 'searched' is required, so an empty region cannot claim there is nothing
// without saying where it looked. Retry and clear-filter are actions you
// pass in, not behaviour the component owns.

What the component takes. This table used to list state, clinical, staleAfter — props the component does not have, and never had. Found by the registry gate’s props-table rule.

PropTypeDefaultNotes
titlestring— Required. What is empty.
searchedstring— Required. What was searched, over what period, from which sources — the proof that it looked.
descriptionstring— Why it may be empty, when that is known.
actionReact.ReactNode— A retry, or a way to widen the search. Yours to supply.
NotJustAnyMed.Tech Design System · Empty state · v1.0 · draft for review
Reference applications named in this system are fictional; all patient data shown is fabricated.