Textarea
Free clinical text — the one field where a clinician says something the system did not anticipate. Everything about it is designed so that what they wrote survives intact and attributed to them.
Overview
Structured fields capture what was expected. A textarea captures everything else — the caveat, the context, the reason the model is wrong. That makes it the highest-value text in the record and the easiest to lose, because it is long, slow to write, and often typed during an interruption.
Never loses text
Nothing a clinician typed disappears — not on validation, not on navigation, not on session lock, not on a lost connection.
Never truncates silently
Limits are visible from the first keystroke. Text is never cut on save, and never cut on display.
Attributed
A draft belongs to whoever typed it. It never crosses an identity boundary without a decision.
Anatomy
| Element | Rule |
|---|---|
| Label | A real <label for>, permanently visible. Same rule as
Text field. |
| Field | Four rows minimum, vertically resizable, never horizontally. Text wraps; it never scrolls sideways. |
| Draft state | When it was last saved and who it belongs to. On a shared workstation the second half matters as much as the first. |
| Counter | Shown from the first keystroke where a limit exists, not from 80 % of it. |
Rules
- Limits are generous and visible. If a limit exists it is shown from the start. A clinician who discovers it at 1 900 characters has already lost the sentence they were composing.
- Never truncate on save. Text over the limit blocks submission with a clear message; it is never quietly cut.
- Never truncate on display either. A note shown elsewhere in the record wraps and expands — see Card.
- Autosave as a draft, with the timestamp and owner visible. Drafts survive navigation, session lock and reconnection.
- No rich text. Bold, bullets and tables do not survive export to most record systems, and what survives is unpredictable. Plain text, plain meaning.
- Spellcheck on for prose, off for identifiers. A clinician typing a drug or an anatomical term should not fight autocorrect; an MRN field should not be spellchecked at all.
- Vertically resizable, never horizontally. A textarea is resizable in both directions by default. Dragging it wider takes it out of the column it was aligned to and introduces the horizontal scrolling the field exists to avoid; dragging it narrower can hide text already written. Height is the reader's to change, width is the layout's.
- Enter inserts a newline. It never submits a form containing a textarea.
Drafts and identity
On a shared workstation an unsaved note is the most likely thing to be misattributed. A draft carries its author from the first keystroke, is never silently adopted by the next user, and on a user switch is either explicitly claimed or discarded with notice — see Sign in / sign out. An assessment recorded under the wrong clinician's name is a defect in the permanent record.
What the component enforces, and what it only asks for
This system says repeatedly that its types are built to make a prohibited thing unexpressible. On this page that is true of some rules and not others, and the difference matters more here than anywhere else. Of the nine rules below, five are held by the component and four are not — three of them are work you have to do, and a reader who assumes otherwise will ship a textarea that loses notes.
| Rule | Held by | How |
|---|---|---|
| Never truncates keystrokes | The component | No maxLength attribute reaches the element. The limit is reported and
enforced at submit, so the sentence being typed survives.Asserted: Textarea: typing past the limit is kept, not swallowed |
| The limit is visible from the first keystroke | The component | Passing maxLength renders the counter; there is no way to set a limit
without showing it.Asserted: Textarea: the counter is described, and the count itself is not live |
| An error never replaces the hint | The component | Separate elements, both named in aria-describedby, error first.Asserted: No component substitutes an error for its hint |
| Resizes vertically, never horizontally | The component | resize-y. The browser default is both directions, which lets a drag
take the field out of the column it was aligned to.Asserted: Textarea: resizes vertically only |
| No placeholder used as a label | The component | placeholder is omitted from the prop type.Asserted: Textarea: placeholder is not in the prop type |
| Autosaves as a draft that survives navigation, session lock and reconnection | You | Nothing in a controlled React input can do this. The component holds no state and has no storage. See Editing the draft. |
| A draft carries its author and never crosses a user switch | You | Identity is not a prop here. See Drafts and identity and Sign in / sign out. |
| Enter inserts a newline and never submits | The platform | True of a real <textarea> and false the moment you add a
keydown handler that submits on Enter. The component does not add one;
nothing stops you. |
| The count is right whether or not the field is controlled | The component | The count read value, which an uncontrolled field does not have: it said 0 / 20 however much was typed. Driven in a browser.Asserted: Textarea: an uncontrolled field still counts what is typed |
| Only a crossing is announced | The component | Into the last tenth, onto the limit, over it. Inside the last tenth the region used to speak on every keystroke — including “1 characters remaining”. Driven in a browser. Asserted: Textarea: the remaining count is announced on crossing, not on every keystroke |
| Spellcheck on for prose, off for identifiers | You | spellCheck passes through. A default cannot be right for both a
free-text impression and a specimen number. |
A rule stated on a component page reads as a rule the component keeps. Three of these are
obligations the page was quietly handing to the reader, and a fourth holds only until someone
adds a keydown handler. Only this table says so. The same
split exists on every component page in this system and is stated on none of them — it is
recorded as a known gap.
1 940 / 2 000
announced once: “60 characters remaining”
The count is on screen throughout and spoken once, on approach. A screen-reader user learns the limit is near without losing the sentence they are composing.
1 940 / 2 000
“1 938 of 2 000” · “1 939 of 2 000” · “1 940 of 2 000” …
A polite live region on the running count. It reads the number after every character, so the note being dictated is buried under its own length.
Do's and don'ts
Counter and owner from the start. The clinician knows the room they have and whose note this is.
A limit revealed by cutting the sentence. The clinical caveat is now half a sentence in a permanent record.
The draft cannot cross an identity boundary without a decision.
Someone else's note silently re-attributed. The record now says Bergström wrote what Whitfield wrote.
Plain text. What the clinician wrote is exactly what reaches the record system.
Rich text formatting that will not survive export, and may arrive in the record as markup a clinician has to read around.
Accessibility
- A real
<textarea>with an associated label. Never acontenteditablediv, which loses form semantics entirely. - The counter is wired with
aria-describedbyand announced at intervals, not on every keystroke. - Vertically resizable and never disabled from resizing, so a magnified user can make room.
- Draft-saved state announces politely, so a screen-reader user knows their work is held.
- Enter never submits. Submission is an explicit button, reachable by keyboard.
- 16 px minimum text, which also prevents zoom-on-focus on touch.
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 note in the record is what the clinician wrote, whole, under their name.
- Efficiency — no re-typing after an interruption, which is the dominant cost of free-text entry in a clinical setting.
- Satisfaction — willingness to write the caveat at all. A field that has lost text once will be used tersely for ever after.
Clinical safety notes
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.
- Never truncates on save or display. (the component) Mitigates: a clinical caveat entering the record as half a sentence.
- Limits visible from the first keystroke. (the component) Mitigates: composition lost at the limit.
- Drafts autosaved and attributed. (you) Mitigates: loss of an assessment across an interruption or session lock.
- Drafts never cross an identity boundary silently. (you) Mitigates: a misattributed clinical note in the permanent record.
- Plain text only. (the component) Mitigates: formatting lost or mangled on export, changing how a note reads.
- Enter does not submit. (the component) Mitigates: a partial note recorded mid-sentence.
Implementation
$ npx shadcn@latest add https://md.notjustany.tech/r/textarea.json
<Textarea
label= "Clinical note"
maxLength={2000} // the counter is shown from the first keystroke
value={value}
onChange={setValue}
/>
// No maxLength reaches the element, so keystrokes past the limit are
// reported rather than swallowed. Autosave, draft ownership and what
// happens on a user switch are yours — see "What the component enforces,
// and what it only asks for" above.
Related
- Text field — labels, hints, validation, identifier rules.
- Clinician override — the optional free-text reason.
- Sign in / sign out — drafts across a user switch.
- Voice & tone — how the surrounding copy is written.