Quick Questions
A few short multiple-choice questions shown together, answered in one go and sent with one button.
≈ 1,165 tokens per use (tool 700 · guidance 270 · typical payload 195)How token costs are estimated
Example: Quick Questions
Help me pack for a wedding in Italy
When to use
- You need 2–5 quick choices before you can tailor the answer, such as when, where and dress code before a packing list.
- Each question has a handful of obvious options the user can tap without thinking.
When not to use
- The questions need explanation, recommendations or free text. Use a Question Stepper, which asks one at a time.
- You can infer the answer from the conversation or from data. Don't ask.
- It's a single yes/no. Ask in one sentence or use Follow-up Chips.
- You're collecting record data, like an address. Use a Generated Form.
How the agent should use it
- Keep it short and plain. A question of a few words ("When are you going?"), options of one to four words. There are no descriptions or recommendations; if an option needs explaining, use a Question Stepper.
- Ask only what changes the answer, most consequential first.
- Add "Not sure yet" as a normal option when the user may not know. It comes back like any other option id.
- Use
type: "multiple"when several answers can be true. Answers are chips; a multiple question is tagged Pick any, and a picked chip takes the violet ring and a check. - Set
allowOtherwhen your options might miss. "Other…" reveals a short input; its answer is"other"and the text comes back inother. Don't useotheras an option id. - Preselect with
defaultsonly what the user already told you. - Write
submitLabelas the outcome, e.g. "Personalize my wardrobe". It's the only button, at the right of the footer; the footer's left says how many are answered ("1 of 3 answered"), and the button's tooltip says how many are left while it's disabled. - Give each question a
header(1–2 words, e.g. "When") so the submitted summary reads "When: September to October · Where: Lake Como". Without it, the summary shows the answer alone. - Lifecycle: none of
expiresAt,fetchedAtor supersede is usually needed. If the agent asks again with different questions, mark the old call superseded by the new one.
Interaction
- Every question is visible at once; the user answers in any order and submits once.
- Questions are required unless
required: false, which shows a quiet "Optional". The button stays disabled until every required question is answered; an empty "Other…" doesn't count. - Each question is a fieldset with its label as the legend, so screen readers announce the question with each option. Radios use native groups: Tab moves between questions, arrow keys between options.
- While streaming, questions appear one by one and can already be answered; the button is disabled until the call finishes.
Installation
npx shadcn@latest add https://your-site.com/r/quick-questions.json
This adds the component, its schema and quick-questions-utils.ts (validation, the result and the summary).
Usage
import { tool } from "ai"
import { quickQuestionsTool } from "@/components/genui/quick-questions.schema"
const tools = {
[quickQuestionsTool.name]: tool({
description: quickQuestionsTool.description,
inputSchema: quickQuestionsTool.inputSchema,
}),
}import { QuickQuestions } from "@/components/genui/quick-questions"
function ToolPart({ part, addToolResult }) {
if (part.type !== "tool-ask_quick_questions") return null
return (
<QuickQuestions
{...part.input}
status={part.state === "input-streaming" ? "streaming" : undefined}
result={part.state === "output-available" ? part.output : undefined}
onResult={(result) =>
addToolResult({
tool: "ask_quick_questions",
toolCallId: part.toolCallId,
output: result,
})
}
/>
)
}The agent receives the option ids, one per single question and a list per multiple question. Unanswered optional questions are left out:
{
action: "submit",
answers: { when: "sep-oct", where: "como", dress: "cocktail" },
}
// With "Other"
{
action: "submit",
answers: { goal: "other", days: "3", equipment: ["dumbbells", "bands"] },
other: { goal: "Train for a half marathon" },
}With LangGraph
Push it from a graph node with ui.push({ name: "ask_quick_questions", props: { toolCallId, ...args } }, { message }), and render the thread's UI messages with the Renderer.
Restoring from history
When a conversation is reloaded, pass the stored result back as result. The component renders the submitted summary ("When: September to October · Where: Lake Como · Dress code: Cocktail") and never calls onResult again, so the answers can't be sent twice. With the AI SDK, that's the tool call's output:
<QuickQuestions
{...part.input}
result={part.state === "output-available" ? part.output : undefined}
/>Schema
| Prop | Type | Notes |
|---|---|---|
title | string? | What the answers are for, e.g. "Before I build your packing list". Default "A few quick questions" |
questions | Question[] | 1–5, ideally 2–5 |
submitLabel | string | The only button, e.g. "Personalize my wardrobe". Max 40 characters |
defaults | Record<questionId, optionId | optionId[]>? | Preselected answers |
Question
| Field | Type | Notes |
|---|---|---|
id | string | Keys the answer in the result |
label | string | The question, e.g. "When are you going?" |
header | string? | 1–2 words for the summary, e.g. "When" |
type | single | multiple | Default single |
options | { id, label }[] | 2–8 |
required | boolean | Default true |
allowOther | boolean | Adds "Other…" with a short input. Default false |
Host props: status (streaming, ready, submitted), onResult, result and className.