Propel

Text Area

A multi-line text field framed by a bordered group, with configurable text size, surface, and resize behavior.

Show code
import { TextArea, TextAreaGroup } from "@makeplane/propel/components/text-area";

export default function BasicDemo() {
  return (
    <TextAreaGroup resize="vertical">
      <TextArea
        size="lg"
        surface="field"
        aria-label="Comment"
        placeholder="Leave a comment..."
        rows={4}
      />
    </TextAreaGroup>
  );
}

Installation

import { TextArea, TextAreaGroup } from "@makeplane/propel/components/text-area";

Usage

import { TextArea, TextAreaGroup } from "@makeplane/propel/components/text-area";

export default function BasicDemo() {
  return (
    <TextAreaGroup resize="vertical">
      <TextArea
        size="lg"
        surface="field"
        aria-label="Comment"
        placeholder="Leave a comment..."
        rows={4}
      />
    </TextAreaGroup>
  );
}

Examples

Sizes

size sets the type, one rung below the frame it sits in — md 12 / lg 13 / xl 14 / 2xl 16 px. Prose in a composer reads smaller than a single-line value at the same nominal size, which is why the two scales are offset. On the field surface each step also pins the frame’s minimum height.

Show code
import { TextArea, TextAreaGroup } from "@makeplane/propel/components/text-area";

const SIZES = ["md", "lg", "xl", "2xl"] as const;

export default function SizesDemo() {
  return (
    <div className="flex flex-col gap-4">
      {SIZES.map((size) => (
        <TextAreaGroup key={size} resize="vertical">
          <TextArea
            size={size}
            surface="field"
            rows={2}
            aria-label={`Comment (${size})`}
            defaultValue={`The ${size} size sizes the value text.`}
          />
        </TextAreaGroup>
      ))}
    </div>
  );
}

Surfaces

surface picks the control presentation: field pairs with the bordered TextAreaGroup frame, embedded pads itself inside a composer frame that owns the chrome, and inline hugs its line height for compact single-row composers.

Show code
import { TextArea, TextAreaGroup } from "@makeplane/propel/components/text-area";

export default function SurfacesDemo() {
  return (
    <div className="flex w-80 flex-col gap-4">
      <TextAreaGroup resize="none">
        <TextArea
          size="lg"
          surface="field"
          aria-label="Field comment"
          rows={2}
          defaultValue="The field surface sits inside the TextAreaGroup frame."
        />
      </TextAreaGroup>
      <div className="flex w-full flex-col overflow-clip rounded-xl border border-subtle-1 bg-layer-2">
        <TextArea
          size="lg"
          surface="embedded"
          aria-label="Embedded comment"
          rows={2}
          defaultValue="The embedded surface pads itself inside a composer frame."
        />
      </div>
      <div className="flex items-center gap-2 rounded-lg border border-subtle-1 bg-layer-2 px-3 py-2">
        <TextArea
          size="lg"
          surface="inline"
          aria-label="Inline comment"
          rows={1}
          defaultValue="The inline surface hugs a compact row."
        />
      </div>
    </div>
  );
}

Resize

TextAreaGroup’s resize prop controls the native drag handle on the bordered frame (so growth carries the chrome): none locks the size, vertical allows height changes, and both frees both axes. The leaf itself is never resizable.

Show code
import { TextArea, TextAreaGroup } from "@makeplane/propel/components/text-area";

const RESIZES = ["none", "vertical", "both"] as const;

const RESIZE_COPY = {
  none: "No resize handle — the textarea keeps its size.",
  vertical: "Drag the corner handle to grow the textarea vertically.",
  both: "Drag the corner handle to grow the framed textarea in both directions.",
} as const;

export default function ResizeDemo() {
  return (
    <div className="flex flex-col gap-4">
      {RESIZES.map((resize) => (
        <TextAreaGroup key={resize} resize={resize}>
          <TextArea
            size="lg"
            surface="field"
            rows={2}
            aria-label={`Comment (resize ${resize})`}
            defaultValue={RESIZE_COPY[resize]}
          />
        </TextAreaGroup>
      ))}
    </div>
  );
}

With description

Visible to everyone in the workspace.

Show code
import { Field, FieldDescription, FieldLabel } from "@makeplane/propel/components/field";
import { TextArea, TextAreaGroup } from "@makeplane/propel/components/text-area";

export default function WithDescriptionDemo() {
  return (
    <Field name="summary">
      <FieldLabel size="lg" inset={false}>
        Project summary
      </FieldLabel>
      <TextAreaGroup resize="vertical">
        <TextArea
          size="lg"
          surface="field"
          rows={3}
          placeholder="Describe what this project is about..."
        />
      </TextAreaGroup>
      <FieldDescription size="lg">Visible to everyone in the workspace.</FieldDescription>
    </Field>
  );
}

API Reference

TextArea

Multi-line text field that automatically works inside `Field`. Base UI's `Field.Control` behavior grafted onto the styled `elements` `TextArea` element (rule 1a) — behavior part outer, styled part as the render target. Base UI ships no textarea control, so `Field.Control` renders the styled `<textarea>` directly (this replaces the former `base/text-area` wrapper). Pair the `field` surface with `TextAreaGroup`, which owns the native `resize` handle.

PropTypeDefaultDescription
size(required)"md" | "lg" | "xl" | "2xl"Text size for the textarea value.
surface(required)"inline" | "field" | "embedded"Control presentation for standalone fields or embedded composer surfaces.
defaultValuestring | number | readonly string[]The default value of the textarea. Use when uncontrolled.
valuestring | number | readonly string[]The value of the textarea. Use when controlled.

TextAreaGroup

The bordered frame that wraps a `field`-surface `TextArea` leaf. Owns the border, radius, focus/error border treatments, and the native `resize` handle so growth carries the chrome with the content. Children should be that single leaf only — the field surface is `min-w-full`, so a sibling inside the frame overflows. Put attach/submit controls outside the group, or use the `embedded` / `inline` surfaces (no group) for composer rows.

PropTypeDefaultDescription
renderReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, {}>Allows you to replace the component's HTML element with a different tag, or compose it with another component. Accepts a `ReactElement` or a function that returns the element to render.
resize"none" | "vertical" | "both"Controls whether the user can resize the framed textarea.