Textarea
A multi-line text input with auto-resize capabilities and URL state support.
Overview
The Textarea component is a multi-line form control for longer text entries like notes, comments, descriptions, and messages. It supports size variants, automatic height adjustment, manual resize modes, and URL state synchronization.
Usage
import { Textarea } from "@tilt-legal/cubitt-components/textarea";<Textarea placeholder="Enter your message..." />Sync the textarea value with the URL by providing paramName:
<Textarea
paramName="message"
paramDebounce={300}
placeholder="Synced with ?message=..."
/>Examples
States
Textarea fields in their default, error, disabled, and readonly states.
import { useState } from "react";
import { Textarea } from "@tilt-legal/cubitt-components/textarea";
import {
ToggleGroup,
ToggleGroupItem,
} from "@tilt-legal/cubitt-components/toggle-group";
const states = ["default", "error", "disabled", "readonly"] as const;
export default function Example() {
const [state, setState] =
useState<(typeof states)[number]>("default");
return (
<>
<div className="w-full max-w-sm">
<Textarea
aria-invalid={state === "error" ? true : undefined}
defaultValue={
state === "readonly" ? "This textarea is read-only." : undefined
}
disabled={state === "disabled"}
key={state}
placeholder={
state === "readonly"
? "Readonly"
: `${state[0]?.toUpperCase()}${state.slice(1)}`
}
readOnly={state === "readonly"}
/>
</div>
<PreviewFooter>
<ToggleGroup
aria-label="Textarea state"
groupVariant="segmented"
multiple={false}
onValueChange={(value) => {
if (states.includes(value as (typeof states)[number])) {
setState(value as (typeof states)[number]);
}
}}
value={state}
>
<ToggleGroupItem value="default">Default</ToggleGroupItem>
<ToggleGroupItem value="error">Error</ToggleGroupItem>
<ToggleGroupItem value="disabled">Disabled</ToggleGroupItem>
<ToggleGroupItem value="readonly">Readonly</ToggleGroupItem>
</ToggleGroup>
</PreviewFooter>
</>
);
}Sizes
import { Label } from "@tilt-legal/cubitt-components/label";
import { Textarea } from "@tilt-legal/cubitt-components/textarea";
export default function Example() {
return (
<div className="flex w-full max-w-sm flex-col gap-6">
<Label>
Small
<Textarea placeholder="Small size" size="sm" />
</Label>
<Label>
Default
<Textarea placeholder="Default size" />
</Label>
<Label>
Large
<Textarea placeholder="Large size" size="lg" />
</Label>
</div>
);
}Auto-resize
Set a minimum row count while keeping automatic height growth.
import { Textarea } from "@tilt-legal/cubitt-components/textarea";
export default function Example() {
return (
<div className="w-full max-w-sm">
<Textarea minRows={3} placeholder="Minimum 3 rows..." />
</div>
);
}Inline
Use variant="inline" when the textarea should read like editable text, such as an inline note, file name, or table cell.
import { Textarea } from "@tilt-legal/cubitt-components/textarea";
export default function Example() {
return (
<div className="w-full max-w-sm">
<Textarea
className="text-fg-2"
defaultValue="Review the uploaded materials and confirm each document is attached to the correct matter before continuing."
minRows={3}
placeholder="Inline paragraph..."
variant="inline"
/>
</div>
);
}URL State
Sync longer text values to the URL with paramName. paramDebounce delays the URL write without slowing the textarea value.
import { Textarea } from "@tilt-legal/cubitt-components/textarea";
export default function Example() {
return (
<div className="w-full max-w-sm">
<Textarea
defaultValue=""
paramDebounce={300}
paramName="note"
placeholder="Type to update the URL"
/>
</div>
);
}API Reference
Textarea
The textarea component with auto-resize and URL state management.
| Prop | Type | Default | Description |
|---|---|---|---|
value | string | — | The controlled value of the textarea. |
defaultValue | string | — | The default value when uncontrolled. |
onChange | (event: ChangeEvent<HTMLTextAreaElement>) => void | — | Callback fired when the value changes. |
placeholder | string | — | Placeholder text when empty. |
size | "sm" | "md" | "lg" | "md" | The size variant of the textarea. |
variant | "default" | "inline" | "default" | The visual variant of the textarea. |
minRows | number | 2 | Minimum number of visible text lines. |
maxRows | number | — | Maximum number of visible text lines. |
resize | true | "none" | "both" | "horizontal" | "vertical" | "block" | "inline" | "none" | Resize behavior of the textarea. |
disabled | boolean | false | Whether the textarea is disabled. |
readOnly | boolean | false | Whether the textarea is read-only. |
aria-invalid | boolean | — | Indicates validation state and applies error styling. |
spellCheck | boolean | false | Enable spell checking. |
className | string | — | Additional CSS classes. |
style | CSSProperties | — | Additional inline styles, merged with textarea resize styles. |
TextareaWrapper
Use TextareaWrapper for embedded textarea compositions that need a shared field surface around inline content.
| Prop | Type | Default | Description |
|---|---|---|---|
children | ReactNode | — | Textarea and embedded content to render inside. |
size | "sm" | "md" | "lg" | "md" | The wrapper size, matching Textarea sizing. |
className | string | — | Additional CSS classes for the wrapper element. |
URL State Props
When provided with a paramName, the textarea syncs its value with URL parameters via TanStack Router search params.
| Prop | Type | Default | Description |
|---|---|---|---|
paramName | string | — | URL parameter name for syncing state with URL. Enables URL state management. |
paramValue | string | — | Controlled value for the URL parameter. |
onUrlValueChange | (value: string | null) => void | — | Callback when URL parameter value changes. |
paramClearOnDefault | boolean | true | Remove URL param when value equals default. |
paramDebounce | number | — | Debounce URL updates in milliseconds. |
paramThrottle | number | — | Throttle URL updates in milliseconds. |