GitHub

Textarea

Multi-line input for longer content with label, helper text, and error states.

01 Live Demo

0 characters

02 Code Snippet

src/ui/Textarea.tsx
import { Textarea } from "@/ui/Textarea";

<Textarea.Root
  label="Project notes"
  description="Write key updates for your team."
  placeholder="Type your notes here..."
  size="md"
/>

03 Copy-Paste (Single File)

Textarea.tsx
import { forwardRef, type TextareaHTMLAttributes } from "react";

type TextareaProps = TextareaHTMLAttributes<HTMLTextAreaElement> & {
  label?: string;
  description?: string;
  error?: string;
};

const cn = (...classes: Array<string | undefined | false>) => classes.filter(Boolean).join(" ");

const TextareaRoot = forwardRef<HTMLTextAreaElement, TextareaProps>(function TextareaRoot(
  { label, description, error, className, ...props },
  ref
) {
  return (
    <div className={cn("flex flex-col gap-1.5", className)}>
      {label && <label className="text-sm font-medium text-slate-700 dark:text-slate-300">{label}</label>}
      <textarea
        ref={ref}
        className={cn(
          "min-h-24 w-full rounded-lg border border-slate-200 bg-white px-3.5 py-2.5 text-sm text-slate-900 outline-hidden",
          "focus:border-primary focus:ring-2 focus:ring-primary/20 dark:border-[#1f2937] dark:bg-[#0d1117] dark:text-white",
          error && "border-red-400 focus:border-red-400 focus:ring-red-400/20"
        )}
        {...props}
      />
      {description && !error && <p className="text-xs text-slate-500 dark:text-slate-400">{description}</p>}
      {error && <p className="text-xs text-red-500 dark:text-red-400">{error}</p>}
    </div>
  );
});

type TextareaCompound = typeof TextareaRoot & { Root: typeof TextareaRoot };
export const Textarea = Object.assign(TextareaRoot, { Root: TextareaRoot }) as TextareaCompound;
react-principles