Textarea

A multiline field for notes, descriptions, and longer text.

fieldform
Preview
Textarea preview
Two lines is enough.

When to use

  • Free-form multi-line text like bios, comments, or feedback.
  • Fields with a live character count, which rolls its digits in the helper row.

When not to use

  • Use input for single-line values.
  • Use inline-edit with multiline for a description edited in place on a page.
  • Use tag-input when the text is really a list of short values.

shadcn CLI

Also installs (1)

  • manicat-foundation
Manual install(+)
registry.jsonjson
{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "textarea",
  "type": "registry:ui",
  "title": "Textarea",
  "description": "A multiline field for notes, descriptions, and longer text.",
  "categories": [
    "inputs"
  ],
  "dependencies": [
    "motion"
  ],
  "registryDependencies": [
    "https://manicat.viraza.net/r/manicat-foundation.json"
  ],
  "files": [
    {
      "path": "registry/components/textarea/textarea.tsx",
      "type": "registry:component",
      "target": "@components/manicat/textarea/textarea.tsx"
    },
    {
      "path": "registry/components/textarea/textarea.module.css",
      "type": "registry:component",
      "target": "@components/manicat/textarea/textarea.module.css"
    }
  ],
  "docs": "Docs and live preview: https://uiarc.dev/components/textarea",
  "meta": {
    "tier": "free",
    "kind": "component",
    "docs": "https://manicat.viraza.net/components/textarea",
    "markdown": "https://uiarc.dev/components/textarea/markdown",
    "tags": [
      "field",
      "form"
    ]
  }
}

Usage

Install the component, then copy this into your app. Change the content and the props to fit your screen.

example.tsxtsx
"use client";

import { useState } from "react";
import { Textarea } from "@/components/manicat/textarea/textarea";

export function TextareaExample() {
  const [value, setValue] = useState("");

  return (
    <div className="grid w-full max-w-md gap-4">
      <Textarea label="Changelog entry" placeholder="What changed?" value={value} onChange={(event) => setValue(event.target.value)} rows={5} />
      <Textarea label="Short note" defaultValue="Kept to two lines." rows={2} description="Two lines is enough." />
    </div>
  );
}

Examples

Real screens rather than isolated props. Switch to Code to copy a whole example into your project.

Feedback form A message box with a live character counter that turns into an error past the limit.

Feedback form A message box with a live character counter that turns into an error past the limit. preview
0/280

Accessibility

  • Native textarea with a real label.
  • Description and error ids are merged into aria-describedby; errors set aria-invalid and use role="alert".
  • Animated copy is aria-hidden and mirrored in a visually hidden plain-text span.

Motion

  • Message rows open their height on a smooth spring; changed words rise in with a soft blur and counts roll only the digits that changed.
  • Reduced motion drops the roll, blur, and height spring for instant changes.

Responsive behavior

  • It fills its column with a 110px minimum height and a vertical resize handle; it does not auto-grow with content.
  • Hover border styles apply only on hover-capable fine pointers.

Performance

  • One ResizeObserver per field measures the helper row for its height spring; cheap for a form, but avoid hundreds in a table.
  • Recomputing a character count on each keystroke only re-renders the helper row words that changed.

Notes for AI

  • Use for free-form, multi-line text. Use input for single lines and inline-edit for text edited in place on a page.
  • A live character count in description gets the rolling-digit treatment for free.