Quick Questions

A few short multiple-choice questions shown together, answered in one go and sent with one button.

Pattern

≈ 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

Before I build your packing list

When are you going?
Where is the wedding?
What's the dress code?

0 of 3 answered

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 allowOther when your options might miss. "Other…" reveals a short input; its answer is "other" and the text comes back in other. Don't use other as an option id.
  • Preselect with defaults only what the user already told you.
  • Write submitLabel as 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, fetchedAt or 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

app/api/chat/route.ts
import { tool } from "ai"
import { quickQuestionsTool } from "@/components/genui/quick-questions.schema"

const tools = {
  [quickQuestionsTool.name]: tool({
    description: quickQuestionsTool.description,
    inputSchema: quickQuestionsTool.inputSchema,
  }),
}
components/message.tsx
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

PropTypeNotes
titlestring?What the answers are for, e.g. "Before I build your packing list". Default "A few quick questions"
questionsQuestion[]1–5, ideally 2–5
submitLabelstringThe only button, e.g. "Personalize my wardrobe". Max 40 characters
defaultsRecord<questionId, optionId | optionId[]>?Preselected answers

Question

FieldTypeNotes
idstringKeys the answer in the result
labelstringThe question, e.g. "When are you going?"
headerstring?1–2 words for the summary, e.g. "When"
typesingle | multipleDefault single
options{ id, label }[]2–8
requiredbooleanDefault true
allowOtherbooleanAdds "Other…" with a short input. Default false

Host props: status (streaming, ready, submitted), onResult, result and className.

Source

On this page