Component
Textarea
Native multi-line textarea styled to the DevSnips input visual language — the reference for the whole family.
- Component
- React
- TSX
- Tailwind CSS
- MIT
What it does
Capabilities
- Semantic HTML
- Native form control
- Native textarea
- Associated labels
- ARIA
Preview
Live component
9:41 100%
devsnips.site/library/react/components/textareas/textarea/ fluid · no fixed width 100%
Install
Add to your project
npx devsnips add React/Components/Textareas/textarea React/Components/Textareas/textarea import type { ReactNode, TextareaHTMLAttributes } from "react";
import { useId } from "react";
function cx(...parts: Array<string | false | null | undefined>): string {
return parts.filter(Boolean).join(" ");
}
/**
* Shared textarea visual language (DevSnips input/textarea tokens):
* full width, 80px minimum height (~3 rows of body-md), vertical-only
* resize, radius-sm, 1px color.border, bg color.input, body-md text,
* muted placeholder, hover/focus surface shift, focus-visible ring from
* color.focus-ring, muted disabled surface, subtle readonly surface,
* 150ms color transitions (off under prefers-reduced-motion).
*/
const TEXTAREA_BASE =
"w-full min-h-[80px] resize-y rounded-[var(--ds-radius-sm)] border bg-[var(--ds-color-input)] px-3 py-2 text-sm leading-5 text-[var(--ds-color-foreground)] shadow-none transition-colors duration-150 ease-out placeholder:text-[var(--ds-color-muted-foreground)] hover:bg-[var(--ds-color-input-hover,var(--ds-color-input))] focus:bg-[var(--ds-color-input-focus,var(--ds-color-input))] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[var(--ds-color-focus-ring)] disabled:pointer-events-none disabled:bg-[var(--ds-color-muted)] disabled:text-[var(--ds-color-muted-foreground)] disabled:opacity-60 read-only:bg-[var(--ds-color-surface-subtle)] read-only:text-[var(--ds-color-muted-foreground)] motion-reduce:transition-none";
const TEXTAREA_BORDER =
"border-[var(--ds-color-border)] focus:border-[var(--ds-color-border-strong)]";
export interface TextareaProps extends TextareaHTMLAttributes<HTMLTextAreaElement> {
/** Visible label; omit and pass `aria-label` for a bare control. */
label?: ReactNode;
}
/**
* The reference textarea. A real native `<textarea>` — form submission,
* keyboard interaction, selection, copy/paste, `name`/`value`, `required`,
* `disabled`, `readOnly`, `minLength`/`maxLength` all keep their native
* behavior. Vertical resize stays enabled (the intentional DevSnips resize
* behavior); `rows` and `min-h-[80px]` give a sensible default height.
* Controlled (`value`/`onChange`) and uncontrolled (`defaultValue`) usage
* work natively — no duplicated state.
*/
export function Textarea({ label, id, className, rows = 3, ...props }: TextareaProps) {
const generatedId = useId();
const textareaId = id ?? `textarea-${generatedId}`;
const control = (
<textarea
id={textareaId}
rows={rows}
className={cx(TEXTAREA_BASE, TEXTAREA_BORDER, className)}
{...props}
/>
);
if (label === undefined || label === null) {
return control;
}
return (
<div className="w-full">
<label
htmlFor={textareaId}
className="mb-2 block text-[13px] font-medium leading-5 text-[var(--ds-color-foreground)]"
>
{label}
</label>
{control}
</div>
);
}
export default Textarea; /* DevSnips React — JavaScript parity build.
* Same API, behavior, and classes as code.tsx; TypeScript types removed.
* Regenerated from code.tsx — edit code.tsx and re-run the generator.
*/
import { useId } from "react";
function cx(...parts) {
return parts.filter(Boolean).join(" ");
}
const TEXTAREA_BASE = "w-full min-h-[80px] resize-y rounded-[var(--ds-radius-sm)] border bg-[var(--ds-color-input)] px-3 py-2 text-sm leading-5 text-[var(--ds-color-foreground)] shadow-none transition-colors duration-150 ease-out placeholder:text-[var(--ds-color-muted-foreground)] hover:bg-[var(--ds-color-input-hover,var(--ds-color-input))] focus:bg-[var(--ds-color-input-focus,var(--ds-color-input))] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[var(--ds-color-focus-ring)] disabled:pointer-events-none disabled:bg-[var(--ds-color-muted)] disabled:text-[var(--ds-color-muted-foreground)] disabled:opacity-60 read-only:bg-[var(--ds-color-surface-subtle)] read-only:text-[var(--ds-color-muted-foreground)] motion-reduce:transition-none";
const TEXTAREA_BORDER = "border-[var(--ds-color-border)] focus:border-[var(--ds-color-border-strong)]";
export function Textarea({ label, id, className, rows = 3, ...props }) {
const generatedId = useId();
const textareaId = id ?? `textarea-${generatedId}`;
const control = <textarea
id={textareaId}
rows={rows}
className={cx(TEXTAREA_BASE, TEXTAREA_BORDER, className)}
{...props}
/>;
if (label === undefined || label === null) {
return control;
}
return <div className="w-full">
<label
htmlFor={textareaId}
className="mb-2 block text-[13px] font-medium leading-5 text-[var(--ds-color-foreground)]"
>
{label}
</label>
{control}
</div>;
}
export default Textarea; # Textarea
Native multi-line textarea styled to the DevSnips input visual language — the reference for the whole family.
## Usage
```tsx
<Textarea label="Project description" placeholder="What does this project do?" rows={4} />
```
## JavaScript
A `code.jsx` build is provided for projects that ship plain JSX. It exposes the same API and behavior as `code.tsx` — only the TypeScript types are removed.
```jsx
<Textarea label="Project description" placeholder="What does this project do?" rows={4} />
```
## Props
| Name | Type | Default | Description |
|---|---|---:|---|
| `label` | `ReactNode` | — | Visible label (omit and pass `aria-label` for a bare control). |
| `value` / `defaultValue` | `string` | — | Controlled / uncontrolled value. |
| `onChange` | `(event) => void` | — | Native change callback. |
| `rows` | `number` | `3` | Visible rows — the natural height floor (with `min-h-[80px]`). |
| `placeholder` | `string` | — | Muted placeholder (never critical information). |
| `disabled` | `boolean` | — | Native disabled — not focusable, not submitted. |
| `readOnly` | `boolean` | — | Native read-only — focusable, selectable, submitted. |
| `required` / `name` / `id` | `boolean` / `string` | — | Native form semantics (`id` also the label `htmlFor`). |
| `minLength` / `maxLength` | `number` | — | Native length constraints. |
| `className` | `string` | — | Extra Tailwind classes merged onto the control. |
| other native props / `aria-*` | — | — | Passed through to the `<textarea>`. |
## States
A real native `<textarea>` styled with Tailwind + the `--ds-*` semantic tokens. Default / hover / focus / focus-visible / filled / disabled / read-only states all come from native pseudo-classes; dark mode follows the token theme. Vertical resize stays enabled (the intentional DevSnips resize behavior — horizontal resize is off so layouts never break). Controlled (`value`/`onChange`) and uncontrolled (`defaultValue`) usage work natively with no duplicated state.
## Accessibility
Real `<textarea>` element — full native keyboard interaction, selection, copy/paste, and form semantics are preserved. Pass `label` for a visible `<label htmlFor>` or `aria-label`/`aria-labelledby` for a bare control. Supporting text uses `aria-describedby`. Visible `focus-visible` ring from `--ds-color-focus-ring`. Disabled uses the real `disabled` attribute; read-only uses real `readOnly`.
## Styling
Built with React, Tailwind CSS, and DevSnips design tokens. The component consumes the `--ds-*` semantic tokens via arbitrary values (for example `bg-[var(--ds-color-input)]`). Define the tokens once in your theme — no component-specific CSS file is required.
## Design Tokens
See [React/DESIGN_TOKENS.md](../../../DESIGN_TOKENS.md) for the authoritative token specification. This textarea uses the semantic color, radius, spacing, and motion tokens.
## Notes
This is the reference implementation for the Textareas family — it establishes the shared full-width layout, `min-h-[80px]` floor, `px-3 py-2` padding, `text-sm` typography, `radius-sm`, 1px `color.border`, `color.input` background, muted placeholder, focus ring, `resize-y` behavior, disabled/read-only treatments, and dark-mode behavior that every other textarea extends. 66 lines UTF-8 · LF · Spaces: 2
DevSnips AI ready Claude · Cursor · Windsurf
Agent skill & Model Context Protocol
Pass this component straight into your AI workflow. DevSnips exposes each resource as a structured, addressable registry entry, so your agent installs the real files instead of approximating them.
devsnips://components/textareas/textarea I want to use this DevSnips resource in production: Page: https://devsnips.site/library/react/components/textareas/textarea/ Install: npx devsnips add React/Components/Textareas/textarea This is the Textarea — Native multi-line textarea styled to the DevSnips input visual language — the reference for the whole family. Use this DevSnips resource as the starting point for my project. Adapt it to my project's stack and conventions, fix any issues, and make it production-ready while preserving its intended design and behavior.
Paste into ChatGPT, Claude or your agent — it already carries the page URL and the exact install path.
Resource URI
devsnips://components/textareas/textarea