Search
Usually the moment a clinician picks a patient — which makes it the most direct route to a wrong-patient error in the whole product, and the reason none of its conveniences are free.
Overview
Consumer search optimises for speed to a result: predictive, forgiving, first-hit-usually-right. Every one of those behaviours is a wrong-patient mechanism when the results are people, and surnames repeat far more often on one ward than designers expect.
So patient search here is deliberately slower: it will not guess, it will not auto-select, and it will not show a result the clinician cannot distinguish from its neighbour.
Never guesses
No auto-selection, no auto-navigation on a single hit, no "did you mean". Selection is always a deliberate act.
Distinguishing
Every result carries enough to tell it apart from a similar one. A list of names is not a list of patients.
Honest about scope
What was searched, and what was not, is stated. A result set with an unstated boundary is indistinguishable from a complete one.
Anatomy
Two Arthur Harlands, born seven years apart, with MRNs differing by a transposed digit. This is the case the component is designed around, and it is not rare.
| Element | Rule |
|---|---|
| Input | autocomplete="off", spellcheck="false", no autocorrect.
Browser autofill on a patient field can suggest a different patient from earlier in
the shift — see Text field. |
| Scope hint | What can be searched and the minimum query length, before typing. |
| Result | Name and date of birth and age and sex and full MRN. All five, always. Never name alone. |
| Footer | Result count and the boundary of the search — the site, the admission status, the time window. |
A result showing only a name is unusable in exactly the situation where care is needed — two patients with the same surname on one ward. Date of birth is the field clinicians actually cross-check against a wristband, so it appears on every result, and the MRN is shown in full and grouped rather than truncated. The same reasoning as Patient header.
Behaviour
- Minimum three characters before any patient query runs. Shorter queries return result sets nobody can meaningfully scan.
- Never auto-select, never auto-navigate — not on a single result, not on an exact MRN match. The clinician picks, always.
- Never reorder results while typing. A list that resequences under the pointer causes the same failure as an unstaged queue insertion — see Data grid.
- Debounce, but never drop keystrokes. The query that runs is the query the clinician finished typing.
- Exact MRN matches rank first and are labelled as exact, but are still selected deliberately.
- No fuzzy matching on identifiers. Fuzzy matching on names is helpful; on an MRN it is a mechanism for returning the wrong patient.
A dropdown of recently viewed patients is convenient and dangerous: it puts the previous patient one keystroke from the current one, on a shared workstation, for a clinician who has been interrupted. If it exists at all it is clearly separated from live results, explicitly labelled recently viewed — not a search result, cleared on user switch, and never the default focus target.
No results, and failed search
The same distinction as Empty state, and for the same reason: "this patient is not here" and "the search did not run" must never look alike.
Searched current admissions at this site. Check the spelling, or search by full MRN. Patients at other sites are not included.
The wording on the failure case is deliberate: "do not conclude the patient is absent". An unavailable search that renders as "no results" is the search equivalent of a failed queue rendering as an empty one.
Both Arthurs shown, both distinguishable, neither pre-selected.
One result, pre-selected, no date of birth, no MRN, and a confidence claim in place of the information needed to check it.
Non-patient search
Searching documentation, settings or an option list is ordinary search and carries none of
the above. It may autocomplete, may highlight a best match, and may act on Enter.
The constraints on this page apply specifically where the result set is people, orders
or clinical records — the component takes a subjects prop so the
distinction is explicit rather than a matter of judgement at each call site.
Do's and don'ts
Both Arthurs shown with dates of birth and full MRNs. Neither pre-selected.
One result, pre-selected, no date of birth, no MRN — and a confidence claim instead of the data needed to check it.
Failure says it is a failure, and says what not to conclude from it.
An unreachable directory rendered as an empty result. The clinician concludes the patient is not here.
Accessibility
- Combobox pattern —
role="combobox"on the input witharia-expandedandaria-controls, results as a listbox, arrow keys to move, Enter to select, Esc to close. - Result count announced politely on change — "3 results" — never each result individually as the list rebuilds.
- Each result's accessible name carries the distinguishing fields, so a screen-reader user hears the date of birth and MRN rather than a bare name.
- Focus never moves to results automatically. The clinician arrows down when ready.
type="search"with a visible label, never a placeholder standing in for one.- Results reachable at 200 % zoom; the list scrolls internally rather than extending past the viewport.
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.
- Effectiveness — the patient opened is the patient intended. This is the component's entire reason for its constraints.
- Efficiency — deliberately traded. Requiring three characters and an explicit selection costs perhaps two seconds, against a wrong-patient event that costs far more.
- Satisfaction — confidence at the moment of selection. A clinician who has to re-check the header after searching has been failed by the search.
Clinical safety notes
Trace these in your risk file (ISO 14971) and usability engineering file (IEC 62366-1).
- No auto-selection or auto-navigation. Mitigates: opening a patient nobody chose.
- Date of birth and full MRN on every result. Mitigates: same-surname mis-selection — the commonest wrong-patient mechanism.
- No fuzzy matching on identifiers. Mitigates: a transposed digit returning a different patient as a near match.
- No browser autofill or autocorrect. Mitigates: a previous patient suggested into the current query.
- Results never reorder while typing. Mitigates: selecting a row that moved.
- Failure distinguished from no-results. Mitigates: an unreachable directory read as "this patient does not exist".
- Search scope stated. Mitigates: a site- or admission-limited result set read as exhaustive.
- Recent searches separated and cleared on user switch. Mitigates: acting on the previous clinician's patient.
Implementation
$ npx shadcn@latest add https://md.notjustany.tech/r/search.json
<Search
label="Find a patient"
subjects="people" // enables every constraint on this page
minLength={3}
scope="Current admissions at this site"
renderResult={(p) => (
<PatientResult
name={p.display} dob={p.dob} age={p.age} sex={p.sex} mrn={p.mrn} />
)}
onSelect={openRecord}
/>
// With subjects="people" the component refuses the conveniences that
// are safe elsewhere.
if (subjects === "people" && (autoSelect || fuzzyIdentifiers)) {
throw new Error(
"[Search] auto-selection and fuzzy identifier matching are not " +
"permitted when results are patients."
)
}
| Prop | Type | Default | Notes |
|---|---|---|---|
subjects | people | records | content |
content |
people and records enforce the clinical constraints. |
minLength | number | 3 for clinical subjects |
Cannot be lowered below 3 for people. |
scope | string | — | Required for clinical subjects. Rendered in the footer. |
onSearchError | (e) => void | — | Renders an Urgent alert, never an empty result set. |
Related
- Patient header — where a selection lands.
- Text field — identifier input rules.
- Empty state — no-results versus failure.
- Sign in / sign out — clearing recents on user switch.