TextArea
Primitive multiline text input component that accepts standard HTML attributes
Usage
import { TextArea } from '@lenso/ui';"use client";
// Adapted from HeroUI v3.2.6 (e385ac202b2cdb94b1bf6fa76d32c31c8259cc5e), Apache-2.0.import { TextArea } from "@lenso/ui";import * as stylex from "@stylexjs/stylex";
const styles = stylex.create({ field: { width: 384, height: 128 } });export function Basic() { return ( <TextArea aria-label="Quick project update" xstyle={styles.field} placeholder="Share a quick project update..." /> );}Local adaptation source above. Derived from HeroUI v3.2.6 source.
For validation, labels, and error messages, see TextField.
Examples
Variants
The TextArea component supports two visual variants:
primary(default) - Standard styling with shadow, suitable for most use casessecondary- Lower emphasis variant without shadow, suitable for use in Surface components
"use client";// Adapted from HeroUI v3.2.6 (e385ac202b2cdb94b1bf6fa76d32c31c8259cc5e), Apache-2.0.import { TextArea } from "@lenso/ui";import * as stylex from "@stylexjs/stylex";const styles = stylex.create({ root: { display: "flex", width: 280, flexDirection: "column", gap: 8 },});export function Variants() { return ( <div {...stylex.props(styles.root)}> <TextArea fullWidth placeholder="Primary textarea" variant="primary" /> <TextArea fullWidth placeholder="Secondary textarea" variant="secondary" /> </div> );}Local adaptation source above. Derived from HeroUI v3.2.6 source.
In Surface
When used inside a Surface component, use variant="secondary" to apply the lower emphasis variant suitable for surface backgrounds.
"use client";// Adapted from HeroUI v3.2.6 (e385ac202b2cdb94b1bf6fa76d32c31c8259cc5e), Apache-2.0.import { Surface, TextArea } from "@lenso/ui";import * as stylex from "@stylexjs/stylex";const styles = stylex.create({ root: { width: "100%", borderRadius: 24, padding: 24 }, field: { width: "100%", minWidth: 280 },});export function OnSurface() { return ( <Surface xstyle={styles.root}> <TextArea xstyle={styles.field} placeholder="Describe your product" variant="secondary" /> </Surface> );}Local adaptation source above. Derived from HeroUI v3.2.6 source.
Full Width
"use client";// Adapted from HeroUI v3.2.6 (e385ac202b2cdb94b1bf6fa76d32c31c8259cc5e), Apache-2.0.import { TextArea } from "@lenso/ui";import * as stylex from "@stylexjs/stylex";const styles = stylex.create({ root: { width: 400 } });export function FullWidth() { return ( <div {...stylex.props(styles.root)}> <TextArea fullWidth placeholder="Full width textarea" /> </div> );}Local adaptation source above. Derived from HeroUI v3.2.6 source.
Controlled
"use client";// Adapted from HeroUI v3.2.6 (e385ac202b2cdb94b1bf6fa76d32c31c8259cc5e), Apache-2.0.import { Description, TextArea } from "@lenso/ui";import * as React from "react";import * as stylex from "@stylexjs/stylex";const styles = stylex.create({ root: { display: "flex", width: 384, flexDirection: "column", gap: 8 },});export function Controlled() { const [value, setValue] = React.useState(""); return ( <div {...stylex.props(styles.root)}> <TextArea aria-describedby="textarea-controlled-description" aria-label="Announcement" placeholder="Compose an announcement..." value={value} onValueChange={setValue} /> <Description id="textarea-controlled-description"> Characters: {value.length} / 280 </Description> </div> );}Local adaptation source above. Derived from HeroUI v3.2.6 source.
Rows and Resizing
"use client";// Adapted from HeroUI v3.2.6 (e385ac202b2cdb94b1bf6fa76d32c31c8259cc5e), Apache-2.0.import { Label, TextArea } from "@lenso/ui";import * as stylex from "@stylexjs/stylex";const styles = stylex.create({ root: { display: "flex", width: 384, flexDirection: "column", gap: 16 }, field: { display: "flex", flexDirection: "column", gap: 8 }, resize: { resize: "vertical" },});export function Rows() { return ( <div {...stylex.props(styles.root)}> <div {...stylex.props(styles.field)}> <Label htmlFor="textarea-rows-3">Short feedback</Label> <TextArea aria-label="Short feedback" id="textarea-rows-3" placeholder="This week's highlights..." rows={3} /> </div> <div {...stylex.props(styles.field)}> <Label htmlFor="textarea-rows-6">Detailed notes</Label> <TextArea aria-label="Detailed notes" id="textarea-rows-6" placeholder="Write out the full meeting notes..." rows={6} xstyle={styles.resize} /> </div> </div> );}Local adaptation source above. Derived from HeroUI v3.2.6 source.
Customization
Tailwind CSS
"use client";// Adapted from HeroUI v3.2.6 (e385ac202b2cdb94b1bf6fa76d32c31c8259cc5e), Apache-2.0.import { TextArea } from "@lenso/ui";import * as stylex from "@stylexjs/stylex";const styles = stylex.create({ field: { height: 112, width: "100%", maxWidth: 320, fontSize: 14, borderRadius: 12, borderWidth: 1, borderStyle: "solid", borderColor: "color-mix(in oklab, var(--border) 80%, transparent)", backgroundColor: "var(--surface)", color: { default: "oklch(26.9% 0 0)", ':is(.dark *, [data-theme="dark"] *)': "oklch(97% 0 0)" }, boxShadow: { default: "0 1px 2px 0 rgb(0 0 0 / .05), 0 0 0 1px rgb(0 0 0 / .05)", ":focus-visible": "0 1px 2px 0 rgb(0 0 0 / .05), 0 0 0 2px oklch(70.8% 0 0 / .25)", ':is(.dark *, [data-theme="dark"] *)': "0 1px 2px 0 rgb(0 0 0 / .05), 0 0 0 1px rgb(255 255 255 / .1)", ':is(.dark *, [data-theme="dark"] *):focus-visible': "0 1px 2px 0 rgb(0 0 0 / .05), 0 0 0 2px oklch(55.6% 0 0 / .3)", }, transitionProperty: "box-shadow, border-color", transitionDuration: "150ms", "::placeholder": { color: { default: "oklch(70.8% 0 0)", ':is(.dark *, [data-theme="dark"] *)': "oklch(55.6% 0 0)", }, }, },});export function CustomStyles() { return <TextArea aria-label="Notes" xstyle={styles.field} placeholder="Add a note..." />;}Local adaptation source above. Derived from HeroUI v3.2.6 source.
Global CSS
Override the shared .textarea class once with Tailwind's @layer components.
@layer components { .textarea { @apply rounded-xl border border-border bg-surface px-4 py-3 text-sm leading-6 shadow-sm;
&:hover, &[data-hovered="true"] { @apply bg-surface-secondary border-border/80; }
&:focus-visible, &[data-focus-visible="true"] { @apply border-accent ring-2 ring-accent/20; }
&[data-invalid="true"] { @apply border-danger bg-danger-soft text-danger; } }}Styling Reference
HeroUI follows the BEM methodology to ensure component variants and states are reusable and easy to customize.
CSS Classes
Base Classes [!toc]
.textarea– Underlying<textarea>element styling
Interactive States
- Hover:
:hoveror[data-hovered="true"] - Focus Visible:
:focus-visibleor[data-focus-visible="true"] - Invalid:
[data-invalid="true"] - Disabled:
:disabledor[aria-disabled="true"]
API Reference
TextArea
TextArea accepts all standard HTML <textarea> attributes plus the following:
| Prop | Type | Default | Description |
|---|---|---|---|
className | string | - | Tailwind classes merged with the base styles. |
rows | number | 3 | Number of visible text lines. |
cols | number | - | Visible width of the text control. |
value | string | - | Controlled value for the textarea. |
defaultValue | string | - | Initial uncontrolled value. |
onChange | (event: React.ChangeEvent<HTMLTextAreaElement>) => void | - | Change handler. |
placeholder | string | - | Placeholder text. |
disabled | boolean | false | Disables the textarea. |
readOnly | boolean | false | Makes the textarea read-only. |
required | boolean | false | Marks the textarea as required. |
name | string | - | Name for form submission. |
autoComplete | string | - | Autocomplete hint for the browser. |
maxLength | number | - | Maximum number of characters. |
minLength | number | - | Minimum number of characters. |
wrap | 'soft' | 'hard' | - | How text wraps when submitted. |
fullWidth | boolean | false | Whether the textarea should take full width of its container |
variant | "primary" | "secondary" | "primary" | Visual variant of the component. primary is the default style with shadow. secondary is a lower emphasis variant without shadow, suitable for use in surfaces. |
For validation props like isInvalid, isRequired, and error handling, use TextField with TextArea as a child component.
Related Showcases
See upstream TextArea showcases. Product showcases are not part of the local component runtime.