Model Picker
betaSearchable popover for choosing an AI model, grouped by provider, with capability icons, context size, speed and cost hints.
Three models
minimal
Searchable, grouped, controlled
Eight models across providers, a disabled tier, badges, capability icons and a controlled open state.
full
Using claude-sonnet-5-5
In a composer toolbar
Opens upward from the composer's toolbar.
composition
Installation
npx shadcn@latest add https://kuro.abstergo.fyi/r/model-picker.jsonCopies the source into components/kura/model-picker with its dependencies (kura-theme, popover, use-controllable-state, utils). Tokens come from the kura-theme item.
When to use it
Use it for
- Let the user switch the model in a chat composer toolbar or a chat header.
- Compare models at a glance: vision/tools/reasoning support, context window, speed and price tier.
- Any “choose one of N models/agents/providers” selector that benefits from search and grouping.
Reach for something else when
- Running actions or navigating — use command-palette.
- Inserting commands while typing a prompt — use slash-command-menu.
- Two or three static options without metadata — a segmented control or plain select is enough.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| models* | readonly ModelPickerModel[] | — | Models: { id, name, provider?, description?, capabilities?, contextWindow?, speed?: 1|2|3, cost?: 1|2|3, badge?, disabled?, icon? }. Grouped by provider in first-seen order. |
| value | string | — | Controlled selected model id. |
| defaultValue | string | — | Initial id when uncontrolled. Defaults to the first enabled model. |
| onValueChange | (id: string, model: ModelPickerModel) => void | — | Called when the user picks a model. |
| open | boolean | — | Controlled open state. |
| defaultOpen | boolean | false | Initial open state. |
| onOpenChange | (open: boolean) => void | — | Open state changes. |
| searchable | boolean | — | Show the search field. Default: true when there are more than 6 models. |
| placeholder | string | "Select model" | Trigger text when nothing is selected. |
| searchPlaceholder | string | "Search models…" | Search field placeholder. |
| emptyMessage | string | "No models match" | Shown when the search has no results. |
| showDetails | boolean | true | Fixed-height footer describing the highlighted model (no layout shift). |
| side | "top" | "bottom" | "bottom" | Preferred popover side; flips on collision. Use top in a bottom-docked composer. |
| align | "start" | "center" | "end" | "start" | Popover alignment relative to the trigger. |
| size | "sm" | "md" | "sm" | Trigger height: sm 32px for toolbars, md 36px. |
All native attributes of the root element, className and ref pass through.
Anatomy
- ModelPickerThe trigger button (provider mark, model name, chevron). data-state = open | closed.
data-slot="model-picker" - ModelPickerContentPopover surface; scales from the trigger (origin-aware).
data-slot="model-picker-content" - ModelPickerSearchSearch row; the input is the combobox that owns keyboard navigation.
data-slot="model-picker-search" - ModelPickerGrouprole=group per provider, labelled by its heading.
data-slot="model-picker-group" - ModelPickerItemrole=option row: name, badge, meta line, capability icons, check. data-active, data-disabled.
data-slot="model-picker-item" - ModelPickerDetailsFooter with the highlighted model's description.
data-slot="model-picker-details"
Accessibility
- Role:
combobox + listbox - The trigger is labelled “Model: <name>”. On open, focus moves to the search combobox (or the listbox when not searchable) and returns to the trigger on close.
- Highlighting uses aria-activedescendant, so focus never leaves the search field while navigating.
- Options expose aria-selected and aria-disabled; provider groups are role=group with their heading as label.
- Capability icons, speed and cost meters carry visually hidden text (“Vision”, “Fast”, “High cost”), so the meta line reads naturally.
- Reduced motion: The popover fades without scale or travel and the chevron flips without rotating; highlight changes are instant in all modes.
| Keys | Action |
|---|---|
| Enter / Space / ArrowDown | Open from the trigger |
| ArrowDown / ArrowUp | Move the highlight (wraps, skips disabled models) |
| Home / End | First / last model (when the search is empty) |
| Enter | Choose the highlighted model and close |
| Escape | Close and return focus to the trigger |
Notes for agents
- Pass `side="top"` inside a composer docked to the bottom of the viewport.
- speed: 3 = fast, 1 = slow/thorough. cost: 1 = cheap, 3 = expensive. Omit either to hide it.
- `icon` takes a 16px provider logo; without it a monogram of the provider is shown.
- formatContextWindow(200000) → "200K" is exported for use elsewhere (e.g. a context-meter).