Skip to content
PlanningPlanMenu

Plan

The agent's proposed plan, from the first streamed step to the last one done: approve, edit or reject it before anything runs.

A coding agent proposes a plan, waits for approval, then works through its todos with subagents folded to one line.

Installation

$ pnpm dlx shadcn@latest add https://chatcn.dev/r/plan.json

Usage

tsx
import { Plan } from "@/components/ui/chatcn/plan"

<Plan
  title="Make /runs paint before the data arrives"
  steps={steps}
  state="awaiting-approval"
  onApprove={(steps) => approve(steps)}
  onReject={(reason) => reject(reason)}
  onStepsChange={setSteps}
/>

state drives everything. While running, steps without an explicit status are derived from currentStepId: earlier steps are done, the current one is highlighted, later ones wait. Pass collapsible once the plan scrolls up the transcript and its header keeps showing Step 3 of 5.

Examples

Every state

Drafting, awaiting approval, running, done, rejected, and steps that failed, blocked or were skipped.

Approval

Nothing runs until the user approves. Reject opens a field for a note, because telling the agent what to change is more useful than a bare no.

Approve plan, edit it first, or reject it with a note for the agent.

Editing

Edit plan turns every step into a line you type in. Enter adds a step below, Backspace in an empty step removes it, and the grip handles reorder by drag or by arrow keys.

Type in any step, press Enter for a new one, drag or arrow-key the handles to reorder.

Streaming

Steps arrive one at a time while the model writes them.

States

StateShows
draftingSteps rise in as they stream, the square caret on the last one, Drafting shimmer
awaiting-approvalNumbered steps, Reject, Edit plan and Approve plan
approvedA quiet confirmation line while the agent starts
runningStatus glyphs, the current step highlighted, Step 2 of 5
doneEvery check drawn, 5 of 5 done
rejectedThe note the user left, nothing ran

Props

PropTypeNotes
titleReactNodeWhat the plan achieves
descriptionReactNodeA sentence or two above the steps
stepsPlanStep[]{ id, title, detail?, status? }
statePlanStateOne of the six states above
currentStepIdstringThe step in progress while running
onApprove(steps) => voidReceives the steps as the user left them
onReject(reason?) => voidThe note typed for the agent
onStepsChange(steps) => voidEnables Edit plan; called on Save plan
rejectedReasonReactNodeShown in the rejected state
editing, defaultEditing, onEditingChangeEdit mode, controlled or not
collapsible, open, defaultOpen, onOpenChangeFold the steps under the header

Keyboard

KeyAction
Enter in a stepAdd a step below and move into it
Backspace in an empty stepRemove it and move to the one above
Alt + ArrowUp, ArrowDown in a stepMove the step
ArrowUp, ArrowDown, Home, End on a handleMove the step
Cmd or Ctrl + EnterSave plan
EscCancel editing, or leave the reject note

Accessibility

  • The plan is a labelled region; steps are an ordered list and the current one has aria-current="step".
  • The state label is a polite live region: screen readers hear Awaiting approval and Step 3 of 5, not every token.
  • Every handle names its step and announces each move: Moved step 2 to position 4 of 5.
  • Step text inputs ignore Enter while an IME is composing, so Japanese and Chinese input never adds stray steps.

Motion

  • Streamed steps rise 4 px and fade in over 200 ms. Steps present at mount appear at once.
  • While dragging, the row follows the pointer 1:1 and the others slide aside over 200 ms; on drop it settles into its slot in 160 ms. Keyboard moves are instant.
  • The highlight moves between steps with a 150 ms background fade. Under reduced motion, only fades remain.