Text area
Multiline entry with auto-growth and soft character limits.
Installation
Import the component in your screen:
import { TextArea } from '@commenda/pathfinder/text-area';Load the stylesheet once in your app root, after host styles:
import '@commenda/pathfinder/styles.css';Pathfinder ships precompiled CSS; no Tailwind setup is required. In Next.js App Router, put interactive state and callbacks in a client component. See installation for the workspace setup.
API reference
| Prop | Type | Behavior |
|---|---|---|
resizeConfig | ObjectView fields · 3 shapes | Auto 80–240px |
characterLimitConfigConditional | ObjectView fields | Soft grapheme limit; excess text is preserved |
maxLength | number | Native UTF-16 hard limit; mutually exclusive with soft limit |
actionConfig | CopyActionView fields | Optional copy action |
labelConditional | string | Visible field label. Supply an accessible name when it is omitted. |
hideLabel | boolean | Visually hide the label while preserving its accessible name. |
aria-labelConditional | string | Accessible name when the field has no visible label. |
requiredConditional | boolean | Mark the field as required. See validation guidance for the component. |
labelConfig | ObjectView fields · 2 shapes | Choose either optionalLabel or help, never both. never excludes the other field. |
description | string | Supporting guidance associated with the field. |
errorConfig | ObjectView fields | Validation message and when it becomes visible. |
disableConfig | DisableConfigView fields | Disabled state with an optional explanation. |
valueConditional | string | — |
onValueChangeConditional | (value: string) => void | — |
defaultValue | string | — |
resizeConfigObject fields
Choose one of these 3 shapes. Do not combine fields excluded by the selected shape.
Option 1
| Field | Type | Requirement |
|---|---|---|
mode | "auto" | Required |
minHeight | number | Optional |
maxHeight | number | Required |
Option 2
| Field | Type | Requirement |
|---|---|---|
mode | "manual" | Required |
minHeight | number | Optional |
maxHeight | number | Optional |
Option 3
| Field | Type | Requirement |
|---|---|---|
mode | "fixed" | Required |
height | number | Required |
characterLimitConfigObject fields
| Field | Type | Requirement |
|---|---|---|
max | number | Required |
actionConfigObject fields
| Field | Type | Requirement |
|---|---|---|
kind | "copy" | Required |
label | string | Optional |
onCopyError | ((error: Error) => void) | Optional |
labelConfigObject fields
Choose one of these 2 shapes. Do not combine fields excluded by the selected shape.
Option 1
| Field | Type | Requirement |
|---|---|---|
optionalLabel | string | Required |
Do not supply: help.
Option 2
| Field | Type | Requirement |
|---|---|---|
help | string | Required |
Do not supply: optionalLabel.
errorConfigObject fields
| Field | Type | Requirement |
|---|---|---|
message | string | Required |
reveal | "after-blur" | "immediate" | Optional |
disableConfigObject fields
| Field | Type | Requirement |
|---|---|---|
isDisabled | boolean | Required |
reason | string | Optional |
Shared field props
| Prop | Type | Behavior |
|---|---|---|
label | string | Visible accessible label; hideLabel hides it visually. |
aria-label | string | Accessible name when a visible label is not supplied. |
value | string | Controlled value; provide the corresponding change callback. |
defaultValue | string | Initial value for an uncontrolled control. |
onValueChange | (value: string) => void | Immediate editing changes; no implicit parsing. |
description | string | Visible supporting guidance. |
required | boolean | Native required semantics and visual asterisk. |
labelConfig | { optionalLabel } or { help } | One annotation; cannot combine with required. |
errorConfig | { message, reveal?: 'after-blur' | 'immediate' } | Replaces guidance after blur by default. |
disableConfig | { isDisabled, reason? } | Disables editing; reason stays visible. |
readOnly | boolean | Preserves focus, selection, and form submission. |
Native name, form, autocomplete, focus/blur handlers, constraints, IDs, ARIA attributes, and typed refs are supported. className styles the outer field. Use onValueChange for text changes. Form reset restores uncontrolled defaults; controlled values remain application-owned.
Usage rules
Minimum authored height is 80px with Copy 13 text. Auto mode grows until its maximum and then scrolls; manual mode resizes vertically. Soft counters count grapheme clusters including emoji and report excess immediately. Hard native limits use browser UTF-16 counting. A composer/send action belongs outside this component.
Limit
Resize
States
Design rules
Figma guidance and reviewed implementation decisions for this component. Rule IDs remain stable for source comments and AI references.
Source rules
| Rule | Source | Contract |
|---|---|---|
| TEXTAREA-001 | 84:363, 159:383 | Minimum 80px, Copy 13, grow to maximum then scroll. |
| TEXTAREA-002 | 159:146, 159:324 | Soft limit permits excess and reports how much; counter opposite support. |
| TEXTAREA-003 | 159:374 | Sending is composer behavior, not TextArea behavior. |
All source IDs refer to The System.
Explicit implementation decisions
True native readOnly remains distinct from disabled despite Figma 158:822 using disabled appearance for non-editable fields. Disabled values do not submit; read-only values do. Disabled editing does not disable the independent copy/reveal actions. Clear cannot modify a disabled/read-only field.
The auto-grow maximum of 240px is a configurable implementation default, not an authored Figma measurement. Soft counters use Unicode graphemes; native maxLength uses browser UTF-16 counting. They are mutually exclusive to avoid contradictory limits.
Error reveal defaults to after-blur; immediate reveal supports submission/server errors. Soft-limit feedback is immediate. Applications own validation schemas and server error clearing. Additional required/help guidance belongs in description when the single label annotation is already occupied.
Optional-label text uses pearl 800 instead of the authored pearl 700: browser axe measured the latter at 3.38:1 on white, below the 4.5:1 requirement for this text.