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.
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
| Prop | Type | Default | Description |
|---|---|---|---|
questions | PlanComposerQuestion[] | — | The agent's clarifying questions. Each has an id, a title, and options of value, label, and description. |
open | boolean | — | Controlled. Plan mode is opened by the application, typically when an agent has questions. |
onOpenChange | (open: boolean) => void | undefined | Requests a visibility change. Cancel and Submit both request close after their own callback. |
onSubmit | (message: string) => void | undefined | Receives a trimmed message. The input clears itself afterwards. |
onComplete | (answers: PlanComposerAnswers) => void | undefined | Receives every answer keyed by question id when the user submits the plan. |
onCancel | () => void | undefined | The user discarded the plan instead of submitting it. |
promptPlaceholder | string | "Ask anything" | Placeholder for the message input while plan mode is closed. |
notePlaceholder | string | "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.