Skip to content
Pinky UI

forms

Expandable Composer

A single-line composer grows into an attached multiline editing surface with tools and a submit path.

  • forms
  • composer
  • textarea
  • progressive
  • editing
Related pattern

Progressive composer expansion; it is not a chat-specific component or modal.

Live preview

Quick usage

usage
import { ExpandableComposer } from "@pinky-ui/systems";

<ExpandableComposer value={draft} onValueChange={setDraft} onSubmit={send} />

Presets

Default

Restrained production behaviour.

Default behaviour

Quiet

Reduced intensity for dense product screens.

disabled={true}

Install

Add @pinky-ui/systems as a dependency, or use the CLI to copy this component's source directly into your project — no dependency to manage, fully editable.

shell
npm install @pinky-ui/systems

Prefer to run the whole repository locally instead?

repository
git clone https://github.com/florash/Pinky-UI.git
cd Pinky-UI
npm install
npm run dev

Props

PropTypeDefaultDescription
onSubmit(value: string) => voidCalled after an intentional submit from the expanded composer.
classNamestringStyles the outer interaction surface.

Accessibility

  • The compact input and expanded textarea share one labelled composer region.
  • Escape closes without discarding draft text; tools and submit remain keyboard reachable.

Performance

  • The richer editor mounts only after focus or content intent and never opens a viewport-level overlay.

Reduced motion

Motion resolves immediately while semantics, focus and state remain unchanged.

When to use

  • Comments, messages, notes and content creation that start as a small prompt

When not to use

  • Full document editing or a modal workflow that needs route-level context

Skill

Purpose

Use Expandable Composer when a message, note or content prompt starts small and needs a richer editor only after intent increases.

Interaction

Begin with one attached line. Focus, typing or Enter opens the multiline surface in place; tools and submit actions appear below the draft. Escape closes the richer state without discarding text.

Usage

tsx
<ExpandableComposer value={draft} onValueChange={setDraft} onSubmit={send} />

Accessibility

Use a labelled input or textarea, real buttons for tools and submit, and preserve the draft when the composer contracts. Do not turn the expansion into an unlabelled dialog.

Reduced motion

Resolve height and opacity immediately while keeping the richer editing surface, tools and submit path available.

Open this skill on its own page