Textarea
Multi-line text field with optional auto-resize, character counter, and clear button.
Renders a native <textarea>; aria-invalid on invalid state; counter announces limit via aria-describedby.
Preview
tsx
import { Textarea } from '@arshad-shah/cynosure-react';
export default function Example() {
return <Textarea placeholder="Write something…" rows={4} />;
}
Auto-resize
Section titled “Auto-resize”Set autoResize to grow the field as the user types. Optionally cap growth with maxRows.
Preview
tsx
import { Textarea } from '@arshad-shah/cynosure-react';
export default function Example() {
return (
<Textarea autoResize maxRows={8} placeholder="Start typing — the field grows automatically…" />
);
}
With character counter
Section titled “With character counter”Set limit to show a character counter and flip aria-invalid when the limit is exceeded.
Preview
tsx
import { Textarea } from '@arshad-shah/cynosure-react';
import { useState } from 'react';
export default function Example() {
const [value, setValue] = useState('');
return (
<Textarea
value={value}
onChange={setValue}
limit={140}
placeholder="What's on your mind? (140 chars max)"
rows={4}
/>
);
}
Invalid state
Section titled “Invalid state” Preview
tsx
import { Textarea } from '@arshad-shah/cynosure-react';
export default function Example() {
return (
<div style={{ display: 'flex', flexDirection: 'column', gap: '0.375rem', width: '400px' }}>
<Textarea invalid defaultValue="This field has an error." rows={3} />
<p style={{ fontSize: '0.875rem', color: 'var(--color-danger-600, #dc2626)', margin: 0 }}>
This field is required.
</p>
</div>
);
}
PropTypeDefaultDescription
rows
number
—
Initial visible row count. Honoured even when `autoResize` is enabled (acts as a minimum).
autoResize
boolean
—
Grow with the content up to `maxRows`. Disables the manual grip when active.
maxRows
number
—
Cap for `autoResize`. Past this row count, the field scrolls.
resize
"vertical"|"horizontal"|"both"|"none"
"vertical"
Corner-grip resize axis. `none` removes the grip entirely.
limit
number
—
Character limit. Soft — typing past it still works but the counter + `aria-invalid` flip.
showCount
boolean
—
Force the counter to render even without a `limit`. Implied when `limit` is set.
clearable
boolean
—
Renders a clear button in the top-right corner (visible once the field has content).
toolbar
ReactNode
—
Footer toolbar contents — rendered inside `<TextareaActions>` on the left of the footer.
className
string
—
style
CSSProperties
—
placeholder
string
—
value
string
—
Controlled value. Pair with `onChange`.
defaultValue
string
—
Uncontrolled initial value.
onChange
((value: string) => void)
—
Called with the next value whenever the user changes the field.
disabled
boolean
—
Disables interaction and dims the field.
readOnly
boolean
—
Renders the field as read-only — value is visible and selectable but not editable.
required
boolean
—
Marks the field as required for form submission.
invalid
boolean
—
Renders the invalid state (red border, error styling) and sets `aria-invalid`.
name
string
—
Submitted form field name.
id
string
—
Element id — auto-generated via `useId` when omitted.
autoFocus
boolean
—
Focuses the control on mount.
size
"sm"|"md"|"lg"
"md"
Control size — affects height, padding, and font size.
variant
"outline"|"filled"|"ghost"
"outline"
Visual treatment. `outline` is the default bordered surface; `filled`
uses a tinted background; `ghost` removes the surface entirely.
Accessibility
Section titled “Accessibility”- Renders a native
<textarea>— inherits all built-in ARIA semantics. invalidsetsaria-invalid="true"on the underlying textarea.- The character counter is linked via
aria-describedbyso screen readers announce the remaining count.
Recipes
Section titled “Recipes”- Use
clearablefor search-like textareas where emptying with one click is helpful. - Use
toolbarto embed action buttons (e.g. formatting controls) in the footer row.