Textarea
A taller input.
import { Textarea } from '@/components/textarea';
export default function TextareaPreview() {
return (
<Textarea
className="w-80"
rows={5}
placeholder="Write your next novel here"
/>
);
} Dependencies
Source Code
'use client';
import type { VariantProps } from 'cva';
import { useEffect, useRef, useState } from 'react';
import { inputStyle } from '@/components/input';
import { composeRefs } from '@/lib/compose-refs';
import { cn } from '@/lib/utils/classnames';
interface TextareaProps extends React.ComponentPropsWithRef<'textarea'> {
invalid?: boolean;
variant?: VariantProps<typeof inputStyle>['variant'];
}
const Textarea = ({ className, invalid, variant, ...props }: TextareaProps) => {
return (
<textarea
data-invalid={invalid}
aria-invalid={invalid}
className={cn(
inputStyle({ variant }),
'h-auto resize-none py-2 leading-snug',
className
)}
{...props}
/>
);
};
// as soon as `field-sizing: content` is supported, we can remove this component and just use the Textarea
/**
* A textarea that resizes as you type.
*/
const TextareaResize = ({ ref, ...props }: TextareaProps) => {
const internalRef = useRef<HTMLTextAreaElement>(null);
const [internalValue, setInternalValue] = useState(props.value);
const value = props.value ?? internalValue;
const handleChange = (event: React.ChangeEvent<HTMLTextAreaElement>) => {
setInternalValue(event.target.value);
props.onChange?.(event);
};
useEffect(() => {
if (internalRef.current) {
internalRef.current.style.height = 'auto';
internalRef.current.style.height = `${internalRef.current.scrollHeight}px`;
}
}, []);
return (
<Textarea
ref={composeRefs(ref, internalRef)}
{...props}
value={value}
onChange={handleChange}
/>
);
};
export { Textarea, TextareaResize }; API Reference
Extends the textarea element.
| Prop | Default | Type |
|---|---|---|
variant | "default" | "default""minimal" |
invalid | - | boolean |
Examples
Default
import { Textarea } from '@/components/textarea';
export default function TextareaPreview() {
return (
<Textarea
className="w-80"
rows={5}
placeholder="Write your next novel here"
/>
);
} Minimal
import { Textarea } from '@/components/textarea';
export default function TextareaMinimalPreview() {
return (
<Textarea
className="w-80"
variant="minimal"
rows={5}
placeholder="Write your next novel here"
/>
);
} Disabled
import { Textarea } from '@/components/textarea';
export default function TextareaDisabledPreview() {
return (
<Textarea
className="w-80"
rows={5}
disabled
value="Once upon a time, in a distant galaxy, there lived a lonely star. Each day it would shine brightly, hoping to catch the attention of passing comets. One day, a beautiful comet noticed its radiant glow and decided to orbit nearby. From that day forward, the star was never lonely again."
/>
);
} Resizable
This is a cross-browser solution for resizing the textarea.
When field-sizing: content is supported, we can remove this component and just use the Textarea.
import { TextareaResize } from '@/components/textarea';
export default function TextareaResizePreview() {
return (
<TextareaResize className="w-80" placeholder="Write your next novel here" />
);
} Best Practices
-
Sizing:
- Consider initial height based on expected content
- Allow resizing when appropriate
- Maintain consistent width with other inputs
-
Accessibility:
- Provide clear labels
- Consider character/word count indicators
Previous
Tabs
Next
Tooltip