Skip to content
Dethink Components

ComponentsTextarea

Textarea

Collect several lines of text, such as a message or description.

On this page

Use this component from the local workspace package. Follow the setup guide first. A public npm package and registry are not available yet.

View registry files

Import the component into your page or component file.

Usage
import { Textarea } from "@dethink/components";

Try the examples, then open the code to use them in your app.

With Field anatomy

Label and helper description wired through Field.

What should the next release improve?

Show sourceexamples/textarea/basic.tsx
examples/textarea/basic.tsx
"use client";

import {
  Field,
  FieldContent,
  FieldControl,
  FieldDescription,
  FieldLabel,
  Textarea,
} from "@dethink/components";

export function TextareaBasic() {
  return (
    <div className="mx-auto max-w-sm">
      <Field id="ta-feedback">
        <FieldContent>
          <FieldLabel>Feedback</FieldLabel>
          <FieldDescription>
            What should the next release improve?
          </FieldDescription>
        </FieldContent>
        <FieldControl asChild>
          <Textarea name="feedback" rows={4} placeholder="Tell us anything…" />
        </FieldControl>
      </Field>
    </div>
  );
}

States, sizes, and resize

Sizes, disabled, invalid, read-only, and resize=none.

Show sourceexamples/textarea/states.tsx
examples/textarea/states.tsx
"use client";

import { Textarea } from "@dethink/components";

export function TextareaStates() {
  return (
    <div className="mx-auto grid max-w-lg gap-4 sm:grid-cols-2">
      <Textarea
        aria-label="Small"
        controlSize="sm"
        rows={2}
        placeholder="Small"
      />
      <Textarea
        aria-label="Large"
        controlSize="lg"
        rows={2}
        placeholder="Large"
      />
      <Textarea
        aria-label="Disabled"
        disabled
        rows={2}
        defaultValue="Disabled content"
      />
      <Textarea
        aria-label="Invalid"
        invalid
        rows={2}
        defaultValue="Too short"
      />
      <Textarea
        aria-label="No resize"
        resize="none"
        rows={2}
        placeholder="resize=none"
      />
      <Textarea
        aria-label="Read-only"
        readOnly
        rows={2}
        defaultValue="Read-only content"
      />
    </div>
  );
}

Examples that combine components for common tasks.

Composer with character budget

The counter shifts from quiet meter to warning to error as the budget runs out, the field flips to invalid past the limit, and a live region keeps assistive tech in sync with the count.

240 left

Show sourceexamples/textarea/recipe-composer.tsx
examples/textarea/recipe-composer.tsx
"use client";

import { useState } from "react";
import {
  Button,
  Field,
  FieldControl,
  FieldError,
  FieldLabel,
  Textarea,
} from "@dethink/components";

const LIMIT = 240;

/**
 * A social-style composer: the counter turns from quiet meter into an error
 * as the budget runs out, and the field flips to invalid past the limit so
 * assistive tech hears the same thing sighted users see.
 */
export function TextareaRecipeComposer() {
  const [text, setText] = useState("");
  const remaining = LIMIT - text.length;
  const overBudget = remaining < 0;

  return (
    <form
      className="mx-auto max-w-sm space-y-3"
      onSubmit={(event) => event.preventDefault()}
    >
      <Field id="composer" invalid={overBudget}>
        <FieldLabel>Share an update</FieldLabel>
        <FieldControl asChild>
          <Textarea
            rows={4}
            resize="none"
            value={text}
            onChange={(event) => setText(event.target.value)}
            placeholder="What are you shipping this week?"
          />
        </FieldControl>
        {overBudget ? (
          <FieldError>
            Update is {-remaining} characters over the limit.
          </FieldError>
        ) : null}
      </Field>
      <div className="flex items-center justify-between">
        <p
          aria-live="polite"
          className={`text-sm tabular-nums ${
            overBudget
              ? "text-destructive font-medium"
              : remaining <= 40
                ? "text-warning"
                : "text-muted-foreground"
          }`}
        >
          {remaining} left
        </p>
        <Button size="sm" disabled={overBudget || text.length === 0}>
          Post update
        </Button>
      </div>
    </form>
  );
}

Textarea renders a real textarea element, so all native attributes apply.

Textarea props
PropWhat it doesDefault
controlSize"sm" | "md" | "lg"Sets the padding and text size to match Input."md"
invalidbooleanShows an error style and marks the field as invalid for screen readers.false
resize"none" | "vertical" | "horizontal" | "both"Sets whether users can resize the field vertically, horizontally, both, or neither."vertical"
…native textarea propsTextareaHTMLAttributesRenders a real <textarea>: rows, name, disabled, readOnly, required, maxLength, and form behavior are native.Not set