opsinjs
ComponentsPatternsAsk users for…

Ask users for medications

A medication is not a string. It is a name, a strength, a form and a schedule, entered by somebody reading a box in poor light.

When to use

Ask for medications when your product does something specific with them: shows a reminder, records adherence, includes them in a summary for a clinician, or checks something that genuinely depends on knowing them.

This is the most error-prone field in a consumer health product, for reasons that are all structural:

  • A medication is at least four facts. They are name, strength, form and schedule. Readers routinely supply one of the four.
  • The name is hard to type. Long, unfamiliar, easily confused, and mangled by autocorrect. Brand and generic names coexist and readers use whichever is on the box.
  • Similar names differ by a syllable, and the reader may not know which one they are on.
  • The source is a physical box, read at arm's length, sometimes by somebody who is unwell.

When not to use

  • You want a complete, reliable medication list. You will not get one from self-entry, and designing as though you will is the underlying mistake. A self-entered list is what the reader believes they are taking. Treat it as that, say so, and never present it as a clinical record.
  • You will do interaction checking. Interaction checking against a self-entered list is a clinical function on unreliable input; that is outside what this design system supports. See Is opsinjs right for this project? and Regulatory context.
  • You only need adherence for one known medicine. Do not build a list; build a single item the reader confirms.
  • You want the daily entry flow. Recording that a dose was taken is Daily logging.

How it works

Diagram source (mermaid)
flowchart TD
A["Add a medication"] --> B{"Is there a trustworthy source to import from?"}
B -->|"yes"| C["Import and let the reader confirm each item"]
B -->|"no"| D["Search a reference list by name"]
D --> E{"Found?"}
E -->|"yes"| F["Select; strength and form offered from the reference entry"]
E -->|"no"| G["Free text<br/>always available, never a dead end"]
F --> H["Reader adds strength, form and schedule"]
G --> H
H --> I["Review: the reader sees exactly what will be stored"]
I --> J["Stored as self-reported, with that provenance visible"]
  • Search, do not spell. Type-ahead against a reference list turns an impossible spelling task into a recognition task. Match on both brand and generic names, and show both in the result so the reader can confirm they are the same thing.
  • Free text is always available. A reader whose medication is not in your list must be able to record it, whether imported, unlicensed, newly approved, or simply missing. A closed list is a dead end at exactly the moment the reader is trying to tell you something important.
  • Never autocorrect a medication name. autocorrect="off", autocapitalize="off", spellcheck="false". Autocorrect turns medication names into unrelated English words with total confidence.
  • Never auto-select a search result. Similar names are the classic medication error, and a UI that picks one for the reader has made a clinical decision it is not entitled to make. The reader selects, explicitly.
  • Strength is a number and a unit, entered separately. 500 and mg, not 500mg as a string. Offer the strengths that exist for the selected product, and allow another.
  • Form matters. The forms are tablet, capsule, liquid, patch, inhaler and injection. The same name at the same strength in two forms is two different things.
  • Schedule in the reader's words, then structured. "Twice a day", "morning and night", "as needed" are how people describe their medicines. Offer those, map them to structure behind the scenes, and never require the reader to fill in a cron expression.
  • Review before storing. A read-back screen showing name, strength, form and schedule, in that order, is the single most effective error check available.
  • Provenance is visible forever. Every entry is marked as self-reported or imported, and from where. See Data provenance and device accuracy.
  • Stopping is as easy as adding, and a stopped medication is retained with its dates rather than deleted. A clinician's next question is usually when it stopped.

Content

Do

"Search for your medicine. It's on the box or the pharmacy label. You can use the brand name or the generic name."

Don’t

"Enter medication name*" as a bare text field with a spell-checker underlining every entry in red.

Do

"This is the list you've told us about. Your clinician's records may show something different. Bring this with you rather than relying on it."

Don’t

"Your medications" presented as an authoritative record, with no indication that it is self-reported.

Medication names are not translated and not sentence-cased; they are written as the source writes them. See Grammar and mechanics.

Accessibility

  • No autofill token exists for a medication. autocomplete="off".
  • The search field is a proper combobox: the result count is announced, the options are reachable with arrow keys, and the selection is announced. A filtered <div> list is unusable with a screen reader.
  • Nothing is selected implicitly. Arrowing through results must not commit one; commitment is explicit, because a wrong commit here has a consequence.
  • The free-text escape is a labelled control always present in the results area, not an option that appears only when the list is empty.
  • Strength and unit are separate labelled controls in a fieldset whose legend names the medication being edited, so the announcement is "Amoxicillin, strength" rather than "strength".
  • Read-back is a real page with headings, not a toast. It is the reader's chance to catch a wrong selection.
  • Errors never clear the entry. Getting a medication name into a field is expensive; losing it is a WCAG 2.2 SC 3.3.7 failure and a practical catastrophe.

Research

Updates to this page

Last read through against the system on 2026-09-20. Due for review every 6 months; expiry is reported by pnpm run check:freshness.

On this page