Components / Inputs & Forms

Textarea

Multi-line text input with optional character count.

Default

With character count

0 / 500

Error state

This field is required.

Source TextareaDemo.tsx

The exact code behind the live demo above. Fetch it raw at /components/source/textarea.txt.

import React, { useState } from "react";

function Section({ title, children }: { title: string; children: React.ReactNode }) {
  return (
    <div className="mb-8">
      <h3 className="text-sm font-bold text-[var(--flux-heading)] mb-3">{title}</h3>
      <div className="p-6 rounded border border-[var(--flux-grey-100)] bg-[var(--flux-surface)]">
        {children}
      </div>
    </div>
  );
}

const labelClass = "block text-sm font-medium text-[var(--flux-black)] mb-1";
const textareaBase =
  "w-full px-3 py-2 rounded border border-[var(--flux-grey-200)] bg-[var(--flux-surface)] text-sm text-[var(--flux-black)] min-h-[100px] resize-y focus:border-[var(--flux-primary-400)] focus:ring-1 focus:ring-[var(--flux-primary-300)] outline-none transition-colors";

const MAX_CHARS = 500;

export default function TextareaDemo() {
  const [charCount, setCharCount] = useState(0);

  return (
    <div>
      <Section title="Default">
        <div className="max-w-md">
          <label className={labelClass}>Description</label>
          <textarea
            className={textareaBase}
            defaultValue="A brief overview of the itinerary."
          />
        </div>
      </Section>

      <Section title="With character count">
        <div className="max-w-md">
          <label className={labelClass}>Notes</label>
          <textarea
            className={textareaBase}
            maxLength={MAX_CHARS}
            onChange={(e) => setCharCount(e.target.value.length)}
          />
          <p className="mt-1 text-xs text-[var(--flux-black)] text-right">
            {charCount} / {MAX_CHARS}
          </p>
        </div>
      </Section>

      <Section title="Error state">
        <div className="max-w-md">
          <label className={labelClass}>Feedback</label>
          <textarea
            className={`${textareaBase} !border-[var(--flux-error)] focus:!ring-[var(--flux-error)]`}
            defaultValue=""
          />
          <p className="mt-1 text-xs text-[var(--flux-error)]">
            This field is required.
          </p>
        </div>
      </Section>
    </div>
  );
}