Interactive components
Create approval flows where the agent pauses and waits for human input.
"use client";// Gen UI Interrupt demo (Built-in Agent port).//// The LangGraph version of this demo uses `useInterrupt` with LangGraph's// native `interrupt()` primitive — the backend pauses the run and surfaces// a payload that the frontend renders into the chat via the `useInterrupt`// hook. The built-in agent (TanStack AI) does NOT have an equivalent// interrupt primitive, so we adapt the demo by registering a frontend tool// with `useFrontendTool`. The handler returns a Promise that only resolves// once the user picks a time (or cancels), which produces the same UX: the// picker appears inline in the chat and the agent's tool call blocks until// the user decides.import React, { useRef } from "react";import { CopilotKitProvider, CopilotChat, useConfigureSuggestions, useFrontendTool,} from "@copilotkit/react-core/v2";import { z } from "zod";import { TimePickerCard, TimeSlot } from "./time-picker-card";const DEFAULT_SLOTS: TimeSlot[] = [ { label: "Tomorrow 10:00 AM", iso: "2026-04-25T10:00:00-07:00" }, { label: "Tomorrow 2:00 PM", iso: "2026-04-25T14:00:00-07:00" }, { label: "Monday 9:00 AM", iso: "2026-04-28T09:00:00-07:00" }, { label: "Monday 3:30 PM", iso: "2026-04-28T15:30:00-07:00" },];type PickerResult = | { chosen_time: string; chosen_label: string } | { cancelled: true };export default function GenUiInterruptDemo() { return ( <CopilotKitProvider runtimeUrl="/api/copilotkit" useSingleEndpoint> <div className="flex justify-center items-center h-screen w-full"> <div className="h-full w-full max-w-4xl"> <Chat /> </div> </div> </CopilotKitProvider> );}function Chat() { // Pending-resolver ref: set by the async handler, called by the render // prop when the user clicks a slot or cancels. This is the built-in agent // adaptation of the LangGraph `resolve(...)` callback. const resolverRef = useRef<((result: PickerResult) => void) | null>(null); useConfigureSuggestions({ suggestions: [ { title: "Book a call with sales", message: "Book an intro call with the sales team to discuss pricing.", }, { title: "Schedule a 1:1 with Alice", message: "Schedule a 1:1 with Alice next week to review Q2 goals.", }, ], available: "always", }); useFrontendTool({ name: "schedule_meeting", description: "Ask the user to pick a time slot for a meeting via an in-chat " + "picker. Blocks until the user chooses a slot or cancels.", parameters: z.object({ topic: z .string() .describe("Short human-readable description of the meeting."), attendee: z .string() .optional() .describe("Who the meeting is with (optional)."), }), // Async handler: returns a Promise that resolves only once the user // acts on the picker. This is the built-in agent shim for LangGraph's // `interrupt()`/`resolve()` pair. handler: async (): Promise<string> => { const result = await new Promise<PickerResult>((resolve) => { resolverRef.current = resolve; }); if ("cancelled" in result && result.cancelled) { return "User cancelled. Meeting NOT scheduled."; } if ("chosen_label" in result) { return `Meeting scheduled for ${result.chosen_label}.`; } return "User did not pick a time. Meeting NOT scheduled."; }, render: ({ args, status }) => { if (status === "complete") return null; const topic = (args as { topic?: string } | undefined)?.topic ?? "a meeting"; const attendee = (args as { attendee?: string } | undefined)?.attendee; return ( <TimePickerCard topic={topic} attendee={attendee} slots={DEFAULT_SLOTS} onSubmit={(result) => { const fn = resolverRef.current; resolverRef.current = null; fn?.(result); }} /> ); }, }); return <CopilotChat className="h-full rounded-2xl" />;}What is this?#
Interactive generative UI creates flows where the agent pauses execution and waits for user input before continuing. This enables approval workflows, confirmation dialogs, and any scenario where human judgment is needed mid-execution.
When should I use this?#
Use interactive generative UI when you need:
- Approval/rejection flows (e.g. "Run this command?")
- User decisions that the agent should know about
- Confirmation dialogs with structured responses
- Any flow where the agent pauses for human judgment
How it works in code#
Not available on this framework. Interactive generative UI requires either a native interrupt primitive or a Promise-resolving frontend tool. For tool-call-based approval flows, use Human-in-the-loop instead.
