Preferences Wizard
A step-by-step setup of the user's lasting preferences, with a review before saving.
≈ 2,540 tokens per use (tool 1,782 · guidance 321 · typical payload 437)How token costs are estimated
Example: Preferences Wizard
Set up my news preferences
Set up your news preferences
For your morning digest and alerts.
Step 1 of 5: Topics
- Topics: current step
- Sources: not started
- Reading depth: not started
- Digest: not started
- Notifications: not started
When to use
- The user wants to set up or personalize lasting preferences the agent will use from now on: news topics, diet, travel style, notifications.
- Onboarding, where a few visual choices replace a long back-and-forth.
When not to use
- The agent needs answers for this one task only. Use a Question Stepper.
- The user is booking, buying or submitting something. Use a Guided Flow.
- The user wants to tune the constraints of an answer the agent already gave. Use Preference Controls.
How the agent should use it
- Keep it to 2–8 short steps, broad to specific, each with a 1–3 word title.
- Prefill
valuewith the user's current settings when you know them; otherwise use sensible defaults. A skipped step keeps them. - Mark a step
requiredonly when you can't work without it. Everything else can be skipped. - Pick the input for the question:
cardswith icons for a handful of broad choices,chipsfor long or open lists,scalefor a degree,rankfor priorities,togglesfor on/off settings,schedulefor days and times. - Only save on
save. Storeanswersas the user's settings, keep your defaults for theskippedsteps, and say in one line what changes. - On
dismiss, save nothing and don't ask again unless the user brings it up. - Lifecycle: no
expiresAtorfetchedAt; preferences don't go stale. If the agent starts a new wizard for the same settings before this one is finished, mark the old call superseded (supersededBy).
Inputs
Every step has id, title, an optional description and required, and one input:
input.type | Shows | Answer |
|---|---|---|
cards | A grid of cards with an icon or image and a check mark. One pick, or several with multiple (min, max) | The option id, or an array of ids |
chips | Toggle chips, plus Add your own with allowCustom | The chosen chips, including the user's own |
scale | The current value, large, over a slider between two words, e.g. Headlines … Deep dives | The number |
rank | A list to reorder by drag or keyboard | Item ids, most important first |
toggles | Switches. An item's description is read to screen readers, not shown | { [itemId]: boolean } |
schedule | Weekday chips and one time (from), or a range (from and to) | { days, from, to? }, days 0 = Sunday … 6 = Saturday |
text | A short free-text answer | The trimmed text |
Interaction
| Input | Action |
|---|---|
| Next | Disabled until the step is valid: required, min and max picks, a day and a time for a schedule. An optional step with nothing picked and no min is a valid "none" |
| Skip | Shown when allowSkip is on and the step isn't required. The step keeps its default and is listed as "Using defaults" |
| Back | Previous step. Answers are kept |
| Review | Replaces Next on the last step. The review lists every answer with an Edit button; editing returns to the review |
| Save preferences | Sends save and leaves a compact record of the answers, marked "Saved to your profile" |
| Not now | Sends dismiss. Nothing is saved |
| Enter | In a single-line input, continues. In Add your own, adds the chip |
| Space / arrows | In a ranking: pick up, move, drop; Escape cancels. Each move is announced |
- Progress you can see. The header shows the icon, "Step 1 of 4" and the step title (with a quiet Required tag when it is), with a segmented bar, one segment per step, under it. Skipped steps are hollow, and the bar's screen reader text names each step as done, skipped, current or not started. For
cardsandchipswith a limit, the footer tracks it on the left ("2 selected · Pick at least 3"), beside a quiet Not now; Back appears from step 2. Option cards show their check only once picked; photo cards are 4:3 with the label on the photo. - Focus follows the step. After moving, focus lands on the step heading, announced as "Step 2 of 5: Cuisines". Steps slide in their direction; with reduced motion they fade.
- Streaming. The header arrives first, then the steps one by one. The user can start straight away; Next on the newest step waits until its options are complete, and the review waits for the whole payload.
Installation
npx shadcn@latest add https://your-site.com/r/preferences-wizard.json
Usage
import { tool } from "ai"
import { preferencesWizardTool } from "@/components/genui/preferences-wizard.schema"
const tools = {
// No execute(): the call is completed by the user in the UI.
[preferencesWizardTool.name]: tool({
description: preferencesWizardTool.description,
inputSchema: preferencesWizardTool.inputSchema,
}),
}import { PreferencesWizard } from "@/components/genui/preferences-wizard"
function ToolPart({ part, addToolResult }) {
if (part.type !== "tool-setup_preferences") return null
return (
<PreferencesWizard
{...part.input}
status={part.state === "input-streaming" ? "streaming" : undefined}
progress={loadProgress(part.toolCallId)}
onProgress={(p) => saveProgress(part.toolCallId, p)}
result={part.state === "output-available" ? part.output : undefined}
onResult={(result) =>
addToolResult({
tool: "setup_preferences",
toolCallId: part.toolCallId,
output: result,
})
}
/>
)
}The agent receives one of:
{
action: "save",
answers: {
topics: ["world", "tech", "climate"],
sources: ["Harbor Post", "My Local Paper"],
depth: 4,
alerts: { breaking: true, followed: false, weekly: true },
},
skipped: ["digest"],
}
{ action: "dismiss" }With LangGraph
Push it from a graph node with ui.push({ name: "setup_preferences", props: { toolCallId, ...args } }, { message }), and render the thread's UI messages with the Renderer.
Restoring from history
There are two ways to restore it, like the Checklist:
| Prop | Behavior | |
|---|---|---|
| Saved progress | onProgress → progress | Save { stepIndex, answers, skipped } on every change and pass it back. A half-done wizard resumes on the same step (stepIndex equal to the number of steps is the review). Nothing is sent to the agent |
| A decision | result | The user saved or chose "Not now". Renders the read-only record and never calls onResult again |
<PreferencesWizard
{...part.input}
progress={savedProgress}
onProgress={(p) => saveProgress(part.toolCallId, p)}
result={part.state === "output-available" ? part.output : undefined}
/>Schema
| Prop | Type | Notes |
|---|---|---|
icon | WizardIcon? | What the preferences are about, in a tinted box beside the step title |
title | string | e.g. "Set up your news preferences". Names the card for screen readers and heads the saved record |
intro | string? | One sentence: why, and what it changes. Read to screen readers, not shown |
steps | Step[] | 2–8 steps |
allowSkip | boolean | Optional steps can be skipped and keep their defaults. Default true |
savedTo | string? | e.g. "your profile". Shown in the saved record and announced on the review |
Step: { id, title, description?, required?, input }. description is one short line under the title; don't repeat the pick limit, which the card already shows.
input.type | Fields | Notes |
|---|---|---|
cards | options: { id, label, description?, icon?, image? }[], multiple?, min?, max?, value? | 2–12 options. icon from a fixed set (newspaper, utensils, plane, music, film, book, dumbbell, leaf, briefcase, heart, globe, sparkles, bell, moon, sun, coffee, shopping-bag, home). image: { src, alt }, http(s) or data:image only |
chips | options: string[], max?, allowCustom?, value? | Always multiple |
scale | min, max, step, value, minLabel, maxLabel, format?, unit?, currency? | format: level ("Adventurous (4 of 5)", default), number, currency, percent |
rank | items: { id, label }[] | 2–8, in the starting order |
toggles | items: { id, label, description?, value }[] | 1–8 |
schedule | days?, from?, to? | Days default to every day, from to 08:00. Set to for a range |
text | placeholder?, maxLength?, value? | Up to 500 characters |
Host props: status (streaming, ready, saved, dismissed), onResult, onProgress, progress, result and className.