Components · Forms

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.

Stable · v1.0 shadcn/ui WCAG 2.2 AA

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

Clinical free text
Draft saved 14:33:08 · S. Whitfield 241 / 2000
ElementRule
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

Drafts and identity

A draft belongs to whoever typed it

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.

RuleHeld by How
Never truncates keystrokesThe 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 keystrokeThe 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 hintThe component Separate elements, both named in aria-describedby, error first.
Asserted: No component substitutes an error for its hint
Resizes vertically, never horizontallyThe 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 labelThe 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 submitsThe 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 controlledThe 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 announcedThe 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 identifiersYou spellCheck passes through. A default cannot be right for both a free-text impression and a specimen number.
Why this is written down

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.

Do

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.

Don't

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

Do
Draft saved 14:33 · S. Whitfield41 / 2000

Counter and owner from the start. The clinician knows the room they have and whose note this is.

Don't
Limit reached — text was shortened

A limit revealed by cutting the sentence. The clinical caveat is now half a sentence in a permanent record.

Do
You have an unsaved note
S. Whitfield started this note. It cannot be recorded under your name.

The draft cannot cross an identity boundary without a decision.

Don't
Signed in as A. Bergström
Your unsaved note has been restored.

Someone else's note silently re-attributed. The record now says Bergström wrote what Whitfield wrote.

Do

Plain text. What the clinician wrote is exactly what reaches the record system.

Don't

Rich text formatting that will not survive export, and may arrive in the record as markup a clinician has to read around.

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/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.
NotJustAnyMed.Tech Design System · Textarea · v1.0 · draft for review
Reference applications named in this system are fictional; all clinical text shown is fabricated.