OptionList
Let the AI propose choices and the human decide. Single or multi-select with keyboard navigation, AI reasoning, selection constraints, and the receipt pattern for post-decision display.
How should we deploy the database migration?
The migration includes 3 schema changes and 1 data backfill.
Installation
npx taw-ui add option-listThis copies the component source and schema into your project. You own the code — customize anything.
Usage
import { tool } from "ai"
import { OptionListSchema } from "@/components/taw/option-list"
export const chooseAction = tool({
description: "Present options for user decision",
parameters: z.object({ context: z.string() }),
outputSchema: OptionListSchema,
execute: async ({ context }) => {
const options = await generateOptions(context)
return {
id: "deploy-strategy",
question: "How should we proceed?",
options: options.map(o => ({
id: o.id,
label: o.label,
description: o.detail,
recommended: o.score > 0.8,
})),
reasoning: "Based on your infrastructure...",
confirmLabel: "Confirm",
}
},
})import { OptionList } from "@/components/taw/option-list"
import { createReceipt } from "taw-ui"
function ToolOutput({ part }) {
const [receipt, setReceipt] = useState()
const handleAction = (id, payload) => {
if (payload.receipt) setReceipt(payload.receipt)
}
return (
<OptionList
part={part}
onAction={handleAction}
receipt={receipt}
/>
)
}Try It
Select an option and click Deploy. The list collapses to a receipt.
How should we deploy the database migration?
The migration includes 3 schema changes and 1 data backfill.
Props
| Field | Type | Description |
|---|---|---|
| part* | TawToolPart | Tool call lifecycle state |
| onAction | (id, payload) => void | Callback for confirm/cancel actions |
| receipt | TawReceipt | Renders the receipt state when provided |
| pending | boolean | Disables all interactions while processing |
| animate | boolean | Enable entrance animations (default: true) |
| className | string | Additional CSS classes on the wrapper |
Schema
Cross-field validation catches duplicate option IDs and invalid minSelections / maxSelections at parse time.
| Field | Type | Description |
|---|---|---|
| id* | string | Stable backend identifier |
| question* | string | The question or prompt shown to the user |
| description | string | Additional context below the question |
| options* | Option[] | 1-10 options to choose from |
| selectionMode | "single" | "multi" | Selection behavior (default: "single") |
| minSelections | number | Minimum selections required (default: 1) |
| maxSelections | number | Maximum selections allowed |
| required | boolean | Whether cancel is hidden (default: true) |
| reasoning | string | AI explanation for these options |
| confirmLabel | string | Confirm button text (default: "Confirm") |
| cancelLabel | string | Cancel button text (default: "Skip") |
| caveat | string | AI caveat or disclaimer |
| source | Source | Data provenance (label + freshness) |
| Field | Type | Description |
|---|---|---|
| id* | string | Unique identifier within the list |
| label* | string | Display label |
| description | string | Detail text below the label |
| badge | string | Custom badge text (e.g. "Recommended") |
| recommended | boolean | Pre-selects and shows green badge if no custom badge |
| disabled | boolean | Prevents selection of this option |
| meta | Record<string, unknown> | Arbitrary metadata passed through to receipts |
Features
Radio buttons for single, checkboxes for multi — controlled by selectionMode
minSelections and maxSelections enforce valid choices, auto-disabling when max is reached
Optional reasoning panel explains why these options were presented
Collapses to a compact card showing selected options after decision
Options marked recommended are selected by default
Duplicate IDs and invalid min/max caught at parse time via .superRefine()
Accessibility
OptionList uses role="listbox" with role="option" on each item, aria-selected, and aria-multiselectable for multi-select mode. Navigation uses roving tabindex so only the focused option is in the tab order.
| Key | Action |
|---|---|
| Arrow Down | Move focus to next enabled option |
| Arrow Up | Move focus to previous enabled option |
| Home | Move focus to first enabled option |
| End | Move focus to last enabled option |
| Enter / Space | Toggle selection on focused option |