Text over several lines (multi-line text field): a comment, a summary, a note to the driver. It can grow with its text and count the characters left.
import { Textarea } from "@umriss-ui/core";
Set rows for the height the text usually needs; the user can drag it taller.
import { FormField, Textarea } from "@umriss-ui/core";
export default function TextArea() {
return (
<FormField label="Incident summary" style={{ maxWidth: 480 }}>
<Textarea rows={3} placeholder="What happened, what customers saw, what is being done" />
</FormField>
);
}
Errors come from the FormField; size="sm" suits dense forms, and disabled text stays readable but fixed.
import { FormField, Grid, Textarea } from "@umriss-ui/core";
export default function States() {
return (
<Grid minItemWidth="260px" gap={4}>
<FormField label="Customer impact" hint="Fixed height, draggable downwards.">
<Textarea
rows={4}
defaultValue={"Checkout answered slowly from 09:40 to 10:10.\nAbout one payment in twelve timed out and was retried by the customer."}
/>
</FormField>
<FormField label="Next update" hint="Small size.">
<Textarea size="sm" rows={2} placeholder="When and where the next update goes out" />
</FormField>
<FormField label="Root cause" error="Give the root cause in at least ten characters.">
<Textarea rows={2} defaultValue="Unknown" />
</FormField>
<FormField label="Timeline" hint="Written by the incident bot.">
<Textarea rows={2} disabled defaultValue="09:41 Alert fired: Checkout p95 above 300 ms." />
</FormField>
</Grid>
);
}
A FormField with an error marks the field invalid by itself. Without one, set invalid and tie your message to the field with aria-describedby.
import { useId, useState } from "react";
import { Stack, Text, Textarea } from "@umriss-ui/core";
export default function WithAnError() {
const [rootCause, setRootCause] = useState("Unknown");
const messageId = useId();
const error = rootCause.trim().length < 10 ? "Give the root cause in at least ten characters." : undefined;
return (
<Stack gap={1} style={{ maxWidth: 420 }}>
<Textarea
aria-label="Root cause"
rows={3}
value={rootCause}
onChange={(event) => setRootCause(event.target.value)}
invalid={error !== undefined}
aria-describedby={error ? messageId : undefined}
/>
{error && (
<Text id={messageId} size="xs" style={{ color: "var(--u-color-danger-text)" }}>
{error}
</Text>
)}
</Stack>
);
}
Set autoGrow so the field grows with its text, and maxRows where it should start to scroll instead.
import { useState } from "react";
import { Button, FormField, Stack, Textarea } from "@umriss-ui/core";
const LONG = Array.from({ length: 12 }, (_, i) => `10:${String(10 + i * 2).padStart(2, "0")} Update ${i + 1}: error rate back under 1 %.`).join("\n");
export default function GrowWithTheText() {
const [notes, setNotes] = useState("09:41 Alert fired.\n09:44 Priya Raman acknowledged.\n09:52 Rolled back release 4.18.");
return (
<FormField label="Responder notes" hint="Grows to at most eight lines." style={{ maxWidth: 520 }}>
<Stack gap={2}>
<Textarea autoGrow maxRows={8} value={notes} onChange={(event) => setNotes(event.target.value)} />
<Stack direction="row" gap={2}>
<Button size="sm" onClick={() => setNotes(LONG)}>
Paste a long log
</Button>
<Button size="sm" variant="ghost" onClick={() => setNotes("")}>
Clear
</Button>
</Stack>
</Stack>
</FormField>
);
}
Set showCount with a maxLength where a text goes somewhere short, such as a status page or a text message.
import { useState } from "react";
import { FormField, Grid, Textarea } from "@umriss-ui/core";
export default function CountTheCharacters() {
const [status, setStatus] = useState("Payments are slower than usual. We are on it.");
const [sms, setSms] = useState(
"Checkout is degraded since 09:40; release 4.18 rolled back, error rate falling, next update at 11:00 on the status page.",
);
return (
<Grid minItemWidth="260px" gap={4}>
<FormField label="Status page headline">
<Textarea showCount maxLength={80} rows={2} value={status} onChange={(event) => setStatus(event.target.value)} />
</FormField>
<FormField label="Text message to on-call" hint="Set from outside, past the limit.">
<Textarea showCount maxLength={100} rows={3} value={sms} onChange={(event) => setSms(event.target.value)} />
</FormField>
</Grid>
);
}
| Key | Action |
|---|---|
Tab |
Moves into the field and on; it is never typed into the text. |
Enter |
Starts a new line; it does not send the form. |
Typing, arrows, Home / End |
The browser's own editing keys; the field adds none. |
A native textarea. Inside a FormField it takes the field's label, hint, required and invalid state; without one, give it an aria-label.
With showCount the characters left stand in a polite live region: a screen reader reads the count when it is free, not after every letter typed.
TextareaProps| Name | Type | Default | Description |
|---|---|---|---|
size | "sm" | "md" | the size of a ControlSizeProvider around it, else "md" | sm for a toolbar and dense forms, md otherwise.Shown in: Configurator, States |
chars | number | — | The width in characters - the length of a line; the field adds its own
padding. Given, the field is that wide wherever it stands, and never
wider than its place. Without it the field fills its place, and is 40
characters wide where the place asks - in a row. Shown in: Configurator |
invalid | boolean | — | Marks the field as invalid. FormField sets it itself as soon as it
carries an error – by hand only needed without FormField.Shown in: Configurator, With an error |
autoGrow | boolean | false | Grows with its content instead of scrolling. Shown in: Configurator, Grow with the text, Resolve an incident (Scenarios) and 1 more |
maxRows | number | — | Upper bound for autoGrow in rows; beyond it the field scrolls.Shown in: Grow with the text, Resolve an incident (Scenarios), Approve spending against a budget (Scenarios) |
showCount | boolean | false | Shows at the bottom right how many characters still fit.
Needs maxLength.Shown in: Count the characters |
resize | "none" | "vertical" | "none" while the field grows by itself, else "vertical" | Whether the browser's handle for dragging is offered. Shown in: Configurator |
ref | Ref<HTMLTextAreaElement> | — | Receives the component's root element. from RefPropShown in: A row that holds still (Sizes), An anchored surface (Popover), Take the width of the trigger (Popover) and 2 more |
Also takes every attribute of <textarea> – without cols.
What umriss deliberately does not build, and why: ADR-0032.