Textarea
A multi-line text field for longer user input.
import { ... } from '@corndev/ui' 0 / 200 characters
HTML
No JavaScript required — these classes are the whole component.
<label class="corn-label" for="message">Message</label>
<textarea id="message" class="corn-textarea" data-size="md" data-resize="vertical"></textarea>
<!-- Grows as it is typed into, starting at 3 lines and scrolling past 12.
No JavaScript: the growing is field-sizing, in the stylesheet. -->
<label class="corn-label" for="description">Description</label>
<textarea
id="description"
class="corn-textarea"
data-autosize
data-max-rows
rows="3"
style="--corn-textarea-max-rows: 12"
></textarea>
Classes
| Class |
|---|
.corn-textarea |
Attributes
| Attribute | Values |
|---|---|
data-size | sm|md|lg |
data-autosize | present or absent |
data-max-rows | present or absent |
data-resize | none|vertical|horizontal|both |
data-error | present or absent |
data-full-width | present or absent |
Vanilla JS
Optional. createTextarea only adds and removes the classes and attributes above, so the HTML is equivalent. Reach for it when you need to change options at runtime.
import { createTextarea } from '@corndev/ui';
import '@corndev/ui/theme';
const el = document.querySelector('#target');
const instance = createTextarea(el, { /* options */ });
Options
| Option | Type | Default | Description |
|---|---|---|---|
size | 'sm'|'md'|'lg' | 'md' | |
disabled | boolean | false | |
error | boolean | false | |
fullWidth | boolean | false | |
autosize | boolean | false | Grows with what is typed, and shrinks again when text is deleted. Pure CSS — field-sizing — so this installs no listener and measures nothing; the markup can carry data-autosize on its own and never call this function. |
rows | number | undefined | Starting height, in lines. With autosize it is the floor the box never goes below; without it, simply the height. Set here it writes the rows attribute, so plain markup can state it instead. |
maxRows | number | undefined | Stops the growth at this many lines and scrolls beyond it. Only meaningful with autosize. |
resize | 'none'|'vertical'|'horizontal'|'both' | undefined | Defaults to vertical, or to none when autosize is set — a drag handle fights a box that sizes itself. |
React
Textarea — from @corndev/ui-react.
The props are the same as the options above, plus className.
Default
<Label htmlFor="message">Message</Label>
<Textarea id="message" placeholder="Enter a longer message..." />Sizes
<Textarea size="sm" aria-label="Small" />
<Textarea size="md" aria-label="Medium" />
<Textarea size="lg" aria-label="Large" />Growing with its content
autosize makes the box follow what is typed into it, and shrink again when
text is deleted. It is done in CSS with field-sizing, so there is nothing to
measure, no listener to install, and the vanilla package gets it from the markup
alone.
<Textarea autosize rows={2} aria-label="Description" />rows is the floor, not just the starting height — the box never goes below
it. Stating it matters more than it looks: under field-sizing an empty
rows={3} textarea would otherwise collapse to a single line, so the stylesheet
sets the minimum explicitly.
The drag handle is turned off by default when autosize is on, since dragging
fights a box that sizes itself. Pass resize to have both.
Without React
The growing is entirely in the stylesheet, so plain markup does it too:
<textarea class="corn-textarea" data-autosize rows="3"></textarea>
Capping the growth
A 200-line SQL definition has to stop growing somewhere. maxRows sets the
ceiling; past it the textarea scrolls as usual.
<Textarea autosize rows={2} maxRows={5} aria-label="SQL definition" />Inside a scrolling panel
This is the case that goes wrong in a real layout. A textarea that grows, inside a panel that grows, inside a page that also scrolls, is how you end up with three scrollbars.
Give the panel a height and let it scroll. The textarea then grows within it and the page never moves — measured in Chromium and Firefox: with 30 lines typed in, the 120px panel stays 120px, the panel scrolls, and the document does not.
<div style={{ blockSize: '120px', overflow: 'auto' }}>
<Textarea autosize rows={2} fullWidth aria-label="Notes" />
</div>A maxRows low enough to fit the panel is the other way round at the same
problem, and the two combine: the box stops growing before it can overflow, and
scrolls its own content instead.
Resize behaviour
<Textarea resize="vertical" aria-label="Notes" />
<Textarea resize="none" aria-label="Notes" />
<Textarea resize="both" aria-label="Notes" />Error state
<Textarea error aria-label="Message" />Disabled
<Textarea disabled aria-label="Message" />