Plan

Display step-by-step task workflows.
Quick Setup
Get started in 3 easy steps
1 of 3 complete
  • Install dependencies
  • Configure environment
  • Run the app
<Plan  id="plan-simple"  title="Quick Setup"  description="Get started in 3 easy steps"  todos={[      {          "id": "1",          "label": "Install dependencies",          "status": "completed"      },      {          "id": "2",          "label": "Configure environment",          "status": "in_progress"      },      {          "id": "3",          "label": "Run the app",          "status": "pending"      }  ]}/>

When the assistant breaks work into steps (a deployment pipeline, a research plan, a migration checklist) the Plan component turns that structure into a visual progress tracker. Each phase shows its status, and the overall shape of the work is scannable at a glance.

Role: State. Shows internal activity and progress. See Design Guidelines for how component roles work.

Getting Started

Run this once from your project root.

npx shadcn@latest add @tool-ui/plan

Create a backend tool that returns a serializable plan payload.

// Backend toolimport { tool } from "ai";const showPlan = tool({  description: "Display an implementation plan to the user",  inputSchema: {    type: "object",    properties: { task: { type: "string" } },    required: ["task"],    additionalProperties: false,  },  async execute({ task }) {    return {      id: "plan-1",      title: "Implementation Plan",      description: "Steps to complete: " + task,      todos: [        { id: "1", label: "Analyze requirements", status: "completed" },        { id: "2", label: "Implement solution", status: "in_progress" },        { id: "3", label: "Write tests", status: "pending" },      ],    };  },});

Drop this into your runtime provider so tool results render as Plan.

import { type Toolkit } from "@assistant-ui/react";import { Plan } from "@/components/tool-ui/plan";import { safeParseSerializablePlan } from "@/components/tool-ui/plan/schema";import { ToolUI, createResultToolRenderer } from "@/components/tool-ui/shared";export const toolkit: Toolkit = {  showPlan: {    type: "backend",    render: createResultToolRenderer({      safeParse: safeParseSerializablePlan,      render: (parsedResult) => (        <ToolUI id={parsedResult.id}>          <ToolUI.Surface>            <Plan {...parsedResult} />          </ToolUI.Surface>          <ToolUI.Actions>            <ToolUI.LocalActions              actions={[                { id: "approve", label: "Approve Plan" },                { id: "revise", label: "Request Changes", variant: "secondary" },              ]}              onAction={(actionId) => console.log(actionId)}            />          </ToolUI.Actions>        </ToolUI>      ),    }),  },};

These snippets use assistant-ui for end-to-end wiring, but the Plan component itself is framework-agnostic at the UI layer: you can use it in any React codebase with any LLM SDK or tool-calling interface that can provide compatible props.

Key Features

Progress visualization

Progress bar and "X of Y complete" at a glance

Status indicators

Distinct icons for pending, in progress, completed, and cancelled

Expandable details

Click any todo to reveal additional context

Completion celebration

Visual feedback when all tasks complete

Props

Prop

Type

Use ToolUI.LocalActions to compose external actions below Plan.

Compact Variant

Use Plan.Compact for a steps-only card body:

  • No title/description header
  • No progress summary or progress bar
  • No muted inner container
  • No embedded footer actions

Todo Item Schema

Prop

Type

Status States

Prop

Type

Accessibility

  • Decorative motion classes (spinner rotation, shimmer, enter/exit effects) are gated behind motion-safe variants
  • The progress bar fill uses a base CSS transition, so completion updates can still animate unless your app adds motion-reduce overrides
  • Functionality and content remain available regardless of motion preferences