Inline card for when an AI agent stops to ask the user something: the question and why, choices with descriptions (one or several), a field for another answer, number-key shortcuts, then a settled 'You answered' line. Answers a client-side ask tool with the Vercel AI SDK.
Set your access key from API keys as NEXTUI_TOKEN in .env.local and add the @nextui namespace below to components.json once. Then every item installs by name. Each install counts as a copy on a free account.
$pnpm dlx shadcn@latest add @nextui/agent-question
1{2"registries": {3"@nextui": {4"url": "https://nextui.dev/r/{name}.json",5"headers": { "Authorization": "Bearer ${NEXTUI_TOKEN}" }6}7}8}
1"use client";23import { useChat } from "@ai-sdk/react";4import { AgentQuestion } from "@/components/nextui/agent-question";56// Server: a tool with no execute, e.g.7// ask_user: tool({ description: "Ask the user when you need a decision",8// inputSchema: z.object({ question: z.string(), options: z.array(z.string()) }) })9export function Messages() {10const { messages, addToolOutput } = useChat();1112return messages.flatMap((message) =>13message.parts.map((part, i) =>14part.type === "tool-ask_user" && part.state !== "input-streaming" ? (15<AgentQuestion16key={i}17question={part.input.question}18options={part.input.options.map((label) => ({ label }))}19answer={part.state === "output-available" ? String(part.output) : null}20onAnswer={(answer) => addToolOutput({ tool: "ask_user", toolCallId: part.toolCallId, output: answer })}21/>22) : null,23),24);25}
| Prop | Type | Default | Description |
|---|---|---|---|
| question | ReactNode | – | What the agent needs to know |
| description | ReactNode | – | One line on why the agent is asking |
| options | { label: string; description?: string }[] | 3 sample segments | The choices; 1–9 on the keyboard pick them |
| multiple | boolean | false | Allow more than one choice; onAnswer then gets an array |
| allowOther | boolean | true | Show a field for an answer that is not in the list |
| otherPlaceholder | string | "Something else…" | Placeholder of that field |
| submitLabel | string | "Answer" | Text of the submit button |
| answer | string | string[] | null | – | Controlled answer; once set the card shows the settled line |
| onAnswer | (answer: string | string[]) => void | – | Called with the chosen label(s) or the typed answer |
| onSkip | () => void | – | Shows a Skip button when set |
| autoPlay | boolean | false | Loop a scripted answer while on screen, for demos |
| className | string | – | Extra classes on the wrapper |
Sign in to see the code
Preview it freely. A free account copies code and prompts a few times a day.