Search documentation

Find a vivid-layer page or product.

Plan Composer

An AI message input that expands into an agent's clarifying questions and returns the answers.

In an agent response

A tool call that waits for the user to complete a plan.

Playback started.

Plan Composer

An AI message input that expands into an agent's clarifying questions and returns the answers.

pnpm dlx shadcn@latest add @vivid-layer/plan-composer

Plan Composer is a faithful React replica of the plan mode input from OpenAI's Codex: the pill-shaped chat composer that unfolds into the agent's clarifying questions before any work starts. It is built for shadcn/ui and Tailwind CSS, installs from the vivid-layer registry as plain TSX in your project, and works with any agent backend — the same plan-mode interaction, wired to your own model.

Plan Composer is a single-line message input that doubles as the surface an agent uses to ask for clarification. Closed, it is a composer: type, press Enter, onSubmit fires. When your agent needs to narrow the work before starting, hand it questions and set open — the pill grows into a panel that walks the user through them one at a time and returns the result to onComplete.

The component owns the interaction and the choreography. It does not own transport, streaming, or when plan mode opens: those stay in your application.

Answering

Each question is a title plus a list of options. Picking an option records it and advances immediately. Options are actions rather than a persistent selection control, and revisiting a question shows the previous pick.

The user can also answer in their own words: every question page ends with its own note field, and Enter or the trailing control commits it as that question's answer. Notes and option picks are interchangeable, and PlanComposerAnswer is a discriminated union so you can tell which you received.

Once every question has an answer, the final page shows a review of the whole plan together with Cancel and Submit. Edit answers returns to the questions with everything intact.

Sessions

A session is one pass through one question set. Exactly two things end it: the panel finishing its close, and you passing a different set of questions. Both clear the answers, drafts and position, so a later request always starts at the first question rather than resuming someone else's plan.

The question id list is what identifies a set, so an inline array is safe — re-rendering with the same questions does not disturb an answer in progress. The message draft is not part of a session and survives plan mode entirely.

API reference

PropTypeDefaultDescription
questionsPlanComposerQuestion[]The agent's clarifying questions. Each has an id, a title, and options of value, label, and description.
openbooleanControlled. Plan mode is opened by the application, typically when an agent has questions.
onOpenChange(open: boolean) => voidundefinedRequests a visibility change. Cancel and Submit both request close after their own callback.
onSubmit(message: string) => voidundefinedReceives a trimmed message. The input clears itself afterwards.
onComplete(answers: PlanComposerAnswers) => voidundefinedReceives every answer keyed by question id when the user submits the plan.
onCancel() => voidundefinedThe user discarded the plan instead of submitting it.
promptPlaceholderstring"Ask anything"Placeholder for the message input while plan mode is closed.
notePlaceholderstring"Answer in your own words"Placeholder for the note field while a question is on screen.

Behavior

The root is a native form, so Enter submits the message and the trailing arrow is a real submit button. Neither submits while an input method editor is composing text.

At rest, only the active composer or plan page is mounted. A page becomes inert as soon as it starts leaving, so its controls cannot be triggered twice during the transition.