{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "textarea",
  "title": "Textarea",
  "author": "Ballmac <https://ui.ballmac.com>",
  "description": "A multi-line text field that can grow with its content between minRows and maxRows, then scroll. Plain resizable mode when autoResize is off.",
  "registryDependencies": [
    "utils"
  ],
  "files": [
    {
      "path": "registry/ballmac/components/textarea.tsx",
      "content": "// Ballmac UI: Textarea. https://ui.ballmac.com/components/textarea\n// Based on shadcn/ui's Textarea (MIT, Copyright (c) 2023 shadcn), restyled and extended with auto-resize.\n\"use client\"\n\nimport * as React from \"react\"\n\nimport { cn } from \"@/lib/utils\"\n\ntype TextareaProps = React.ComponentProps<\"textarea\"> & {\n  /** Grow and shrink with the content instead of scrolling. */\n  autoResize?: boolean\n  /** Smallest height in rows when autoResize is on. Defaults to `rows` or 3. */\n  minRows?: number\n  /** Largest height in rows when autoResize is on; past it the textarea scrolls. */\n  maxRows?: number\n}\n\nconst useIsomorphicLayoutEffect = typeof window === \"undefined\" ? React.useEffect : React.useLayoutEffect\n\nfunction Textarea({\n  className,\n  autoResize = false,\n  minRows,\n  maxRows,\n  rows,\n  ref,\n  onChange,\n  ...props\n}: TextareaProps) {\n  const innerRef = React.useRef<HTMLTextAreaElement | null>(null)\n  const min = minRows ?? rows ?? 3\n\n  const resize = React.useCallback(() => {\n    const el = innerRef.current\n    if (!el || !autoResize) return\n    const cs = window.getComputedStyle(el)\n    const px = (value: string) => parseFloat(value) || 0\n    const lineHeight = px(cs.lineHeight) || px(cs.fontSize) * 1.5 || 24\n    const border = px(cs.borderTopWidth) + px(cs.borderBottomWidth)\n    const chrome = px(cs.paddingTop) + px(cs.paddingBottom) + border\n    const minHeight = min * lineHeight + chrome\n    const maxHeight = maxRows ? maxRows * lineHeight + chrome : Infinity\n    el.style.height = \"auto\"\n    const content = el.scrollHeight + border\n    el.style.height = `${Math.min(Math.max(content, minHeight), maxHeight)}px`\n    el.style.overflowY = content > maxHeight + 1 ? \"auto\" : \"hidden\"\n  }, [autoResize, min, maxRows])\n\n  // Re-measure on mount, on controlled value changes and when the width changes (line wrapping).\n  useIsomorphicLayoutEffect(() => {\n    resize()\n  })\n  React.useEffect(() => {\n    const el = innerRef.current\n    if (!el || !autoResize || typeof ResizeObserver === \"undefined\") return\n    let width = el.offsetWidth\n    const observer = new ResizeObserver(() => {\n      if (el.offsetWidth !== width) {\n        width = el.offsetWidth\n        resize()\n      }\n    })\n    observer.observe(el)\n    return () => observer.disconnect()\n  }, [autoResize, resize])\n\n  const setRef = React.useCallback(\n    (node: HTMLTextAreaElement | null) => {\n      innerRef.current = node\n      if (typeof ref === \"function\") ref(node)\n      else if (ref) ref.current = node\n    },\n    [ref]\n  )\n\n  return (\n    <textarea\n      ref={setRef}\n      data-slot=\"textarea\"\n      data-autoresize={autoResize || undefined}\n      rows={autoResize ? min : rows}\n      onChange={(event) => {\n        onChange?.(event)\n        resize()\n      }}\n      className={cn(\n        \"flex min-h-16 w-full min-w-0 rounded-md border border-input bg-background px-3 py-2 text-sm leading-6 text-foreground shadow-xs outline-none transition-[color,border-color,box-shadow] duration-150 selection:bg-primary selection:text-primary-foreground placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:bg-input/30\",\n        autoResize && \"min-h-0 resize-none\",\n        className\n      )}\n      {...props}\n    />\n  )\n}\n\nexport { Textarea, type TextareaProps }\n",
      "type": "registry:ui",
      "target": "@components/ballmac/textarea.tsx"
    }
  ],
  "meta": {
    "schema": 1,
    "tier": "free",
    "tags": [
      "textarea",
      "form",
      "autosize",
      "multiline"
    ],
    "version": "1.0.0",
    "updated": "2026-09-28",
    "ai": {
      "summary": "Multi-line text input. Set autoResize with minRows and maxRows for chat composers and comment boxes; leave it off for a user-resizable box with a fixed rows count.",
      "whenToUse": [
        "Comments, feedback and descriptions",
        "Chat or prompt composers that grow as the user types (autoResize)",
        "Notes fields in settings forms"
      ],
      "whenNotToUse": [
        "Single-line values (use input)",
        "Rich text or markdown editing with formatting controls"
      ],
      "composesWith": [
        "label",
        "button",
        "dialog"
      ],
      "a11y": [
        {
          "keys": "Tab",
          "action": "Moves focus to the textarea"
        },
        {
          "keys": "Enter",
          "action": "Inserts a new line"
        }
      ],
      "customization": [
        "autoResize: grows with content; minRows (default rows or 3) and maxRows cap the height",
        "aria-invalid=\"true\" switches to destructive styling",
        "Works controlled (value/onChange) or uncontrolled"
      ]
    },
    "source": {
      "name": "shadcn/ui Textarea",
      "url": "https://github.com/shadcn-ui/ui",
      "license": "MIT",
      "copyright": "Copyright (c) 2023 shadcn",
      "modified": true
    },
    "examples": [
      "textarea-demo",
      "textarea-autosize"
    ],
    "url": "https://ui.ballmac.com/components/textarea"
  },
  "categories": [
    "primitives"
  ],
  "type": "registry:ui"
}