Question

Pro

An agent asking you something mid-task. A few options with a line of why, a recommended pick, room for your own answer, and a receipt that folds to one line once you reply. The answer goes back to the agent as the tool result.

Koala needs your input

Which database should I set up for the orders service?

Installation#

# One-time setup (tokens + lib helpers)npx koalaui-cli@latest init# Activate your Pro license (once per machine)npx koalaui-cli@latest login <key># Add this component (its dependencies come along)npx koalaui-cli@latest add question

Usage#

This example is part of Koala UI Pro

The preview is yours to explore. Its code comes with a Pro license, paid once and used in every project.

Have a license? Unlock it here

Anatomy#

A header that says who is asking, the title with the question itself, the content that folds away once you reply (options, steps, actions) and the answer receipt that opens in its place. Keep the title outside the content and it stays as the receipt's heading.

import {  Question,  QuestionHeader,  QuestionIcon,  QuestionLabel,  QuestionStepper,  QuestionTitle,  QuestionContext,  QuestionContent,  QuestionSteps,  QuestionStep,  QuestionOptions,  QuestionOption,  QuestionOther,  QuestionActions,  QuestionSkip,  QuestionBack,  QuestionSubmit,  QuestionAnswer,} from "@/components/ui/question"<Question>  <QuestionHeader>    <QuestionIcon />    <QuestionLabel />    <QuestionStepper />  </QuestionHeader>  <QuestionContent>    <QuestionSteps>      <QuestionStep>        <QuestionTitle />        <QuestionContext />        <QuestionOptions>          <QuestionOption />          <QuestionOther />        </QuestionOptions>      </QuestionStep>    </QuestionSteps>    <QuestionActions>      <QuestionSkip />      <QuestionBack />      <QuestionSubmit />    </QuestionActions>  </QuestionContent>  <QuestionAnswer /></Question>

Single and multiple#

type="single" is a radio group: arrows move the pick, and the row's number key turns into a check. type="multiple" is a toggle group with a checkbox per row, so it reads as “pick any” before you touch it. A picked row is a faint chip in full ink, never the brand color.

Single select

Where should the preview deploy?

Multiple select

Which checks should run before I open the PR?

Pick any. Each one adds about a minute.

Your own answer#

QuestionOther is the escape hatch for when none of the options fit. Picking it opens a field in place, right under the row, and the typed text goes back in place of the option. Submit stays disabled while the field is empty.

Koala needs your input

How should people sign in?

Several questions#

Wrap each question in a QuestionStep inside QuestionSteps. The header's stepper counts them, Next becomes Submit on the last one, and the frame tweens to each step's height so the change registers. onSubmit gets one key per step.

Before I scaffold the app

Which database?

What should ship in the first version?

Pick any. The rest go on the roadmap.

Where does it deploy?

States#

Four states. Asking is the open card. Once answered, skipped or expired, the content folds away, the icon turns into a check, a forward arrow or an hourglass, and the receipt opens in its place. Expired is yours to set, when the agent stops waiting and goes on without you.

Koala needs your input

Which database should I set up?

Koala asked

Which database should I set up?

You chosepostgres

Koala asked

Which database should I set up?

Skipped. The agent will use its best judgment.

Koala asked

Which database should I set up?

No answer in time. The question expired.

Receipts#

A flow's receipt is a row per step, named by the step's label. Pass the stored answer as defaultValue with status="answered" to rebuild it from chat history; the options mount folded, so the receipt still reads their labels.

Before I scaffold the app

You choseNo answer

In a chat#

Inside a Chat turn, use variant="plain". It drops the frame, so the question reads as part of the answer, and the header can go too: the message already says who is asking.

Add order history to the account page.

There is no orders table yet, so I need to create one first. One decision before I write the migration:

Which database should I set up for the orders service?

Density#

Like Plan and Tool, Question reads the density context (or a density prop). It tunes the frame's padding and the option rows, which follow DropdownMenu's heights at each density.

comfortable

Which database?

compact

Which database?

Keyboard#

Arrow keys move between options. 1 to 9 pick the option with that number from anywhere in the question, outside the text field. Enter on an option or in the Other field submits, or goes to the next step. Space toggles a row in a multiple question. Typing on a picked Other row jumps into its field.

API reference#

Question

The root: status, the answers and the step on show, which every part reads. Every part accepts className, merged last, and every part is droppable. Forwards native <div> props.

PropTypeDefaultDescription
status"asking" | "answered" | "skipped" | "expired"-Controlled status. QuestionSubmit and QuestionSkip move it; set expired yourself when the agent stops waiting.
defaultStatusQuestionState"asking"Initial status, uncontrolled.
onStatusChange(status: QuestionState) => void-Called on submit (answered) and skip (skipped).
valueRecord<string, string[]>-Controlled selection, keyed by step value (or "answer" without steps). A picked Other is the QUESTION_OTHER marker.
defaultValueRecord<string, string[]>-Initial selection, uncontrolled. Pass the stored answer to rehydrate a receipt.
onValueChange(value: Record<string, string[]>) => void-Called on every pick.
defaultOtherRecord<string, string>-Initial Other text per key.
stepstring-Controlled step on show, by QuestionStep value.
defaultStepstring-Initial step. The first step when omitted.
onStepChange(step: string) => void-Called on Next and Back.
onSubmit(answers: Record<string, string[]>) => void-The tool result. Option values per key, with the typed text in place of Other.
onSkip() => void-Called when the person skips.
variant"card" | "plain""card"card frames the question; plain drops the frame for a Chat turn.
density"comfortable" | "compact"-Padding and option row height. Inherits the nearest DensityProvider (compact with none).
asChildbooleanfalseRender your own element as the root.
Data attributeDescription
data-statusThe question's status.

QuestionHeader

The top line: icon, label, stepper. Forwards native <div> props.

QuestionIcon

A 16px glyph that morphs to a check, a forward arrow or an hourglass when the question is answered, skipped or expired. Defaults to the question mark. aria-hidden.

QuestionLabel

Who is asking, muted. Renders a <span>.

QuestionStepper

2/3 in tabular-nums, the current number rolling as you step. Renders nothing for a single question or once answered.

PropTypeDefaultDescription
label(current: number, total: number) => stringQuestion ${current} of ${total}The accessible wording.

QuestionTitle

The prompt. Labels the options it belongs to. Outside QuestionContent it stays in the receipt as its heading. Renders an <h3>.

PropTypeDefaultDescription
asChildbooleanfalseRender your own heading level.

QuestionContext

Why the agent asks, muted, tucked under the title. Describes the options. Renders a <p>.

QuestionContent

Everything that folds away once answered: options, steps, actions. Inert when folded. Forwards native <div> props.

Data attributeDescription
data-stateopen while asking, closed after.

QuestionSteps

Holds the steps of a flow and tweens its height between them, so each step lands at its own size. Forwards native <div> props.

QuestionStep

One question of a flow. Only the active one shows; it slides in from the side you are heading. A role="group" <div>.

PropTypeDefaultDescription
value*string-The key its answer is stored under.
labelstring-Short name for the receipt row (Database). Falls back to value.
Data attributeDescription
data-activePresent on the step on show.
data-directionforward or back, after the first move.

QuestionOptions

The choices. A Radix RadioGroup for single, a Radix ToggleGroup for multiple: arrows move, number keys pick, Enter submits.

PropTypeDefaultDescription
type"single" | "multiple""single"One answer, or any number.
namestring-Overrides the key. Defaults to the step's value, or "answer".
loopbooleantrueArrow keys wrap around.

QuestionOption

One choice, on DropdownMenu's row anatomy. Its number key sits on the right and turns into a check when picked; multiple shows a checkbox instead. Renders a <button>.

PropTypeDefaultDescription
value*string-What goes back to the agent.
descriptionReact.ReactNode-One line on what the choice means.
recommendedboolean | stringfalseAdds a Recommended chip, or your own wording.
textValuestring-The receipt's wording when the children are not a plain string.
Data attributeDescription
data-selectedPresent when picked.

QuestionOther

The last row: your own answer. Picking it reveals a field in place, which takes focus. Its text replaces the marker in onSubmit.

PropTypeDefaultDescription
labelstring"Other"The row's label.
descriptionReact.ReactNode-One line under the label.
placeholderstring"Type your answer"The field's placeholder.
inputPropsReact.ComponentProps<"input">-Forwarded to the field.
Data attributeDescription
data-stateopen while the field shows.

QuestionActions

The button row. Skip sits on the left, Back and Submit on the right. Forwards native <div> props.

QuestionSkip

A ghost Skip. Moves the status to skipped. Forwards Button props.

QuestionBack

A ghost Back. Renders nothing on the first step. Forwards Button props.

QuestionSubmit

The primary action. Reads Next until the last step, then Submit, with the label rolling between them. Disabled until the step is answered.

PropTypeDefaultDescription
childrenReact.ReactNode"Submit"The last step's label.
nextLabelReact.ReactNode"Next"The label on every other step.

QuestionAnswer

The receipt. Opens once the question is no longer asking: You chose Postgres, a row per step for a flow, a note when skipped or expired. A role="status" <div>.

PropTypeDefaultDescription
chosenLabelReact.ReactNode"You chose"The lead-in of a single answer.
skippedLabelReact.ReactNode-The skipped note.
expiredLabelReact.ReactNode-The expired note.
emptyLabelReact.ReactNode"No answer"Shown for a step left blank.
childrenReact.ReactNode-Your own receipt instead.
Data attributeDescription
data-stateopen once answered, skipped or expired.

FAQ#

onSubmit receives an object with one key per question and an array of values per key, the typed text in place of Other. Hand it to your SDK as the tool result: addToolResult in the Vercel AI SDK, the tool_result content block with the Anthropic API.