This determines which frameworks and tools are available.
<QuestionFlow id="question-flow-project-setup" steps={[ { "id": "language", "title": "Select a programming language", "description": "This determines which frameworks and tools are available.", "options": [ { "id": "python", "label": "Python" }, { "id": "typescript", "label": "TypeScript" }, { "id": "go", "label": "Go" } ] }, { "id": "framework", "title": "Choose a framework", "description": "Pick the framework you're most comfortable with.", "options": [ { "id": "fastapi", "label": "FastAPI" }, { "id": "django", "label": "Django" }, { "id": "flask", "label": "Flask" } ] }, { "id": "database", "title": "Select your database", "description": "Your data will be stored and queried from here.", "options": [ { "id": "postgres", "label": "PostgreSQL" }, { "id": "mysql", "label": "MySQL" }, { "id": "mongodb", "label": "MongoDB" } ] } ]} onComplete={(answers) => console.log("Complete:", answers)}/>Run this once from your project root.
npx shadcn@latest add @tool-ui/question-flowRender QuestionFlow in your UI with tool-compatible props.
import { QuestionFlow } from "@/components/tool-ui/question-flow";export function Example({ payload }: { payload: React.ComponentProps<typeof QuestionFlow> }) { return <QuestionFlow {...payload} />;}Register this renderer so tool results display as QuestionFlow.
"use client";import { type Toolkit } from "@assistant-ui/react";import { QuestionFlow } from "@/components/tool-ui/question-flow";import { safeParseSerializableQuestionFlow, SerializableQuestionFlowSchema,} from "@/components/tool-ui/question-flow/schema";import { createArgsToolRenderer } from "@/components/tool-ui/shared";export const toolkit: Toolkit = { configureProject: { description: "Guide user through project configuration.", parameters: SerializableQuestionFlowSchema, render: createArgsToolRenderer({ safeParse: safeParseSerializableQuestionFlow, idPrefix: "wizard", render: (parsedArgs, { result, addResult }) => result ? ( <QuestionFlow id={parsedArgs.id} choice={{ title: "Project configured", summary: Object.entries(result as Record<string, string[]>).map( ([key, values]) => ({ label: key, value: values.join(", "), }), ), }} /> ) : ( <QuestionFlow {...parsedArgs} onComplete={(answers) => addResult?.(answers)} /> ), }), },};These snippets use assistant-ui for end-to-end wiring, but the QuestionFlow 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.
AI generates each step dynamically based on previous answers
Define all steps upfront, component manages navigation internally
Each step can allow one choice or multiple selections
Display a summary of completed configuration choices
QuestionFlow has two modes:
Pass step, title, and options to render a single step. The AI controls progression by generating new steps based on user selections.
Pass steps with all step definitions. The component manages internal state and navigation.
Set selectionMode="multi" to allow multiple selections per step.
Pass choice with a title and summary to show what was configured.
The receipt state:
Prop
Type
Prop
Type
Prop
Type
Prop
Type
Prop
Type