- Accordion
- Avatar
- Badge
- Breadcrumb
- Button
- Calendar
- ChatContainer
- ChatInput
- ChatMessage
- ChatMultiChoiceQuestion
- ChatMultiOptionQuestion
- ChatThinking
- Checkbox
- Combobox
- Container
- CurrencyInput
- DistributionSlider
- Drawer
- Dropdown
- FilePicker
- Grid
- Heading
- Image
- Input
- InputGroup
- Label
- Logo
- MapPin
- Markdown
- Modal
- NativeSelect
- NumberInput
- OptionSlider
- OtpInput
- PhoneInput
- Popover
- Progress
- PropertyCalendar
- RadioGroup
- RadioGroupCards
- ResponsiveModal
- ScrollArea
- SearchBar
- SearchBarFallback
- SearchInput
- Select
- Separator
- Spinner
- Switch
- Table
- Tabs
- Text
- Textarea
- TimePicker
- Toast
- Toggle
- ToggleCard
- ToggleGroup
- Toolbar
- Tooltip
ChatMultiChoiceQuestion
An assistant-message single-select question with an optional free-form fallback, modeled after Claude's AskUserQuestion tool
pnpm add @wandercom/design-system-web
import { ChatMultiChoiceQuestion } from '@wandercom/design-system-web/ui/chat-multi-choice-question';
import { useState } from 'react';
export function Example() {
const [value, setValue] = useState<string | null>(null);
return (
<ChatMultiChoiceQuestion
question="Photos"
description="Use your current photos, or have us curate new imagery that fits your brand."
options={[
{ value: 'new', label: 'Use new high quality photos' },
{ value: 'existing', label: 'Use my existing photos' },
]}
value={value}
onChange={setValue}
onSubmit={(answer) => respond(answer)}
/>
);
}ChatMultiChoiceQuestion is the assistant-side counterpart to a chat composer: an assistant message poses a question, the user picks one of the offered options (or, optionally, types a free-form answer), and the response is submitted back to the conversation. Selecting an option auto-submits via onSubmit — there is no dedicated submit button by default.
A vertical stack of single-select option cards. The currently selected option is highlighted with a filled surface and a trailing arrow.
<ChatMultiChoiceQuestion
question="Photos"
description="Use your current photos, or have us curate new imagery that fits your brand."
options={[
{ value: 'new', label: 'Use new high quality photos' },
{ value: 'existing', label: 'Use my existing photos' },
]}
value={value}
onChange={setValue}
/>A free-form text input renders by default below the options, separated by a divider — wire it up with otherValue / onOtherChange. Submitting the input (Enter or the trailing arrow button) calls onSubmit with { other: string }. Pass allowUserInput={false} to hide it.
<ChatMultiChoiceQuestion
question="Photos"
description="Use your current photos, or have us curate new imagery."
options={[
{ value: 'new', label: 'Use new high quality photos' },
{ value: 'existing', label: 'Use my existing photos' },
]}
value={value}
onChange={setValue}
allowUserInput
otherValue={other}
onOtherChange={setOther}
onSubmit={(answer) => {
if (typeof answer === 'string') respondWithChoice(answer);
else respondWithText(answer.other);
}}
/>For full control over layout — for example, dropping the prompt because the assistant message already provides context — use the compound subcomponents directly.
import {
ChatMultiChoiceQuestionRoot,
ChatMultiChoiceQuestionPrompt,
ChatMultiChoiceQuestionOptions,
ChatMultiChoiceQuestionOther,
ChatMultiChoiceQuestionSubmit,
} from '@wandercom/design-system-web/ui/chat-multi-choice-question';
<ChatMultiChoiceQuestionRoot
options={options}
value={value}
onChange={setValue}
allowUserInput
otherValue={other}
onOtherChange={setOther}
onSubmit={respond}
>
<ChatMultiChoiceQuestionPrompt description="Use your current photos, or have us curate new imagery.">
Photos
</ChatMultiChoiceQuestionPrompt>
<ChatMultiChoiceQuestionOptions />
<ChatMultiChoiceQuestionOther placeholder="Tell us what you'd prefer" />
</ChatMultiChoiceQuestionRoot>;The compound subcomponents are also exposed under the namespace export: ChatMultiChoiceQuestion.Root, .Prompt, .Options, .Other, .Submit.
question?:
description?:
options:
value?:
onChange?:
allowUserInput?:
otherValue?:
onOtherChange?:
onSubmit?:
disabled?:
className?:
Root provides selection state and submit context to its compound subcomponents and owns the radiogroup wrapper. Use this when you need full layout control — the top-level ChatMultiChoiceQuestion covers the common case.
options:
value?:
onChange?:
allowUserInput?:
otherValue?:
onOtherChange?:
onSubmit?:
disabled?:
aria-label?:
asChild?:
children?:
className?:
children
description?:
asChild?:
className?:
asChild?:
className?:
placeholder?:
inputAriaLabel?:
submitAriaLabel?:
hideDivider?:
asChild?:
className?:
requireAnswer?:
children?:
...ButtonProps
- The root renders
role="radiogroup"with anaria-labelledbyreference to the prompt heading when one is provided. - Each option is a
<button role="radio">witharia-checked. Only the currently selected option (or the first option, when no selection has been made) is in the tab sequence (tabIndex={0}); other options aretabIndex={-1}and reachable via arrow keys. - Keyboard interaction:
Arrow Down/Arrow Right— move focus to the next option (wraps).Arrow Up/Arrow Left— move focus to the previous option (wraps).Home/End— focus the first / last option.Enter/Space— activate the focused option (native button behavior).
- Disabled options are skipped during arrow navigation.
- The free-form
Otherrow reusesChatInputInline: pressingEnteron the input submits the value, and the trailing send button is disabled while the input is empty. - The trailing arrow icon on the selected option is
aria-hidden— selection is communicated viaaria-checked.
- Auto-submit on selection mirrors Claude's
AskUserQuestionbehavior. If you need an explicit submit step, droponSubmitfromRootand renderChatMultiChoiceQuestion.Submityourself; the button reads selection state from context. - When
allowUserInputis true and the user types a non-empty value, the submit button on theOtherrow activates regardless of whether a card is also selected — selecting a card still auto-submits the card value.