InputsTextarea
Textarea
The multi-line text input. Used for long-form feedback, descriptions, and the open-response fields in the HCI study.
Demo
The default is 4 rows tall, with a 500 character cap and a live counter.
Default
0 / 500
States
States
Anatomy
- Same border, padding, and radius as the Input
- Resize:
verticalonly. No horizontal resize. - Character counter is
labelstyle, 0.6875 rem, 0.1 em tracking, uppercase. - Line height 1.5, font 0.9375 rem.
Implementation
textarea.tsx
import { forwardRef, useEffect, useRef, type TextareaHTMLAttributes } from "react"
import { cn } from "@/lib/utils"
interface TextareaProps extends TextareaHTMLAttributes<HTMLTextAreaElement> {
autoResize?: boolean
invalid?: boolean
maxLength?: number
}
export const Textarea = forwardRef<HTMLTextAreaElement, TextareaProps>(
function Textarea({ autoResize, invalid, maxLength, className, value, onInput, ...props }, ref) {
const innerRef = useRef<HTMLTextAreaElement | null>(null)
useEffect(() => {
if (!autoResize || !innerRef.current) return
const el = innerRef.current
el.style.height = "auto"
el.style.height = el.scrollHeight + "px"
}, [value, autoResize])
return (
<textarea
ref={(node) => {
if (typeof ref === "function") ref(node)
innerRef.current = node
}}
className={cn("ds-textarea", invalid && "ds-textarea--invalid", className)}
maxLength={maxLength}
aria-invalid={invalid || undefined}
value={value}
onInput={onInput}
{...props}
/>
)
},
)Props
| Prop | Type | Default | Description |
|---|---|---|---|
rows | number | 4 | Visible row count |
autoResize | boolean | false | Grow with content |
maxLength | number | None | Maximum characters |
invalid | boolean | false | Error state |