Textarea
A multi-line text field in the same frame as Input, with an invalid state and vertical resizing.
stableTextarea.tsx
Examples
Basic
Markdown is supported.
textarea/basic.tsx
import { Textarea } from "@streamuilib/react";
export default function TextareaBasic() {
return (
<div className="w-full max-w-sm">
<label htmlFor="textarea-feedback" className="mb-2 block font-bold">
Feedback
</label>
<Textarea
id="textarea-feedback"
placeholder="What should we build next?"
aria-describedby="textarea-feedback-hint"
/>
<p id="textarea-feedback-hint" className="mt-2 text-sm text-ink-muted">
Markdown is supported.
</p>
</div>
);
}Invalid and disabled
Write at least 20 characters.
textarea/states.tsx
import { Textarea } from "@streamuilib/react";
export default function TextareaStates() {
return (
<div className="flex w-full max-w-sm flex-col gap-6">
<div>
<label htmlFor="textarea-bio" className="mb-2 block font-bold">
Bio
</label>
<Textarea id="textarea-bio" rows={3} defaultValue="Hi" invalid aria-describedby="textarea-bio-error" />
<p id="textarea-bio-error" className="mt-2 text-sm font-semibold">
Write at least 20 characters.
</p>
</div>
<div>
<label htmlFor="textarea-locked" className="mb-2 block font-bold">
Admin notes
</label>
<Textarea id="textarea-locked" rows={2} defaultValue="Only admins can edit this." disabled />
</div>
</div>
);
}Installation
bun add @streamuilib/reactusage.tsx
import { Textarea } from "@streamuilib/react";
<label htmlFor="msg">Message</label>
<Textarea id="msg" rows={4} />API
| Prop | Type | Default | Description |
|---|---|---|---|
| invalid | boolean | false | Sets aria-invalid and tints the field pink. |
| rows | number | 4 | Visible lines before scrolling. |
| frameClassName | string | — | Classes for the outer frame. |
| className | string | — | Classes for the <textarea> itself. |
| ...props | ComponentProps<"textarea"> | — | Every native textarea attribute. |
Accessibility
- Pair it with a <label>, and link hints or errors with aria-describedby.
- Shares Input's frame, so focus, invalid, and disabled states look the same across a form.
Source
packages/react/src/Textarea.tsx
import type { ComponentProps } from "react";
import { cn } from "./cn";
import { fieldFrame } from "./field";
export type TextareaProps = ComponentProps<"textarea"> & {
/** Marks the field invalid (sets `aria-invalid`) and tints it. */
invalid?: boolean;
/** Classes for the outer frame. `className` goes to the `<textarea>`. */
frameClassName?: string;
};
export function Textarea({ invalid, frameClassName, className, rows = 4, ...props }: TextareaProps) {
return (
<div className={fieldFrame({ invalid, className: frameClassName })}>
<textarea
rows={rows}
aria-invalid={invalid || undefined}
className={cn(
"w-full min-w-0 resize-y bg-transparent px-3 py-2.5 leading-relaxed placeholder:text-ink-muted focus:outline-none disabled:cursor-not-allowed",
className,
)}
{...props}
/>
</div>
);
}