Model Selector

The picker for which model answers. A searchable list grouped by provider, where each row carries what decides the choice: what the model is for, what it can do and how much context it holds.

Installation#

# One-time setup (tokens + lib helpers)npx koalaui-cli@latest init# Add this component (its dependencies come along)npx koalaui-cli@latest add model-selector

Usage#

usage.tsxTSX
import {  ModelSelector,  ModelSelectorTrigger,  ModelSelectorContent,  ModelSelectorInput,  ModelSelectorList,  ModelSelectorGroup,  ModelSelectorItem,  ModelSelectorLogo,  ModelSelectorName,  ModelSelectorCapabilities,  ModelSelectorCapability,  ModelSelectorMeta,} from "@/components/ui/model-selector"export function Picker() {  const [model, setModel] = React.useState("claude-sonnet-5-5")  return (    <ModelSelector value={model} onValueChange={setModel}>      <ModelSelectorTrigger>Sonnet 5.5</ModelSelectorTrigger>      <ModelSelectorContent>        <ModelSelectorList>          <ModelSelectorGroup heading="Anthropic">            <ModelSelectorItem value="claude-sonnet-5-5">              <ModelSelectorName>Claude Sonnet 5.5</ModelSelectorName>            </ModelSelectorItem>          </ModelSelectorGroup>        </ModelSelectorList>      </ModelSelectorContent>    </ModelSelector>  )}

Anatomy#

import {  ModelSelector,  ModelSelectorTrigger,  ModelSelectorContent,  ModelSelectorInput,  ModelSelectorList,  ModelSelectorGroup,  ModelSelectorItem,  ModelSelectorLogo,  ModelSelectorName,  ModelSelectorCapabilities,  ModelSelectorCapability,  ModelSelectorMeta,} from "@/components/ui/model-selector"<ModelSelector>  <ModelSelectorTrigger>    <ModelSelectorLogo />  </ModelSelectorTrigger>  <ModelSelectorContent>    <ModelSelectorInput />    <ModelSelectorList>      <ModelSelectorGroup>        <ModelSelectorItem>          <ModelSelectorLogo />          <ModelSelectorName addon={<ModelSelectorCapabilities>            <ModelSelectorCapability />          </ModelSelectorCapabilities>} />          <ModelSelectorMeta />        </ModelSelectorItem>      </ModelSelectorGroup>    </ModelSelectorList>  </ModelSelectorContent></ModelSelector>

In a composer#

The trigger is a ghost Button at sm, so it sits on PromptInput's toolbar like every other control there. It shows what you give it, usually the provider's mark and the model's short name, rather than mirroring the row: the toolbar wants “Opus 5.5”, the list wants the full name. Its natural neighbour is the Context Meter, which turns over when you switch to a model with a different window.

ModelSelectorInput is optional. Leave it out when the list fits on one screen. Focus then lands on the list itself, so the arrow keys still walk the rows and Enter still chooses.

API reference#

ModelSelector

The root: the selection and the panel's open state. Choosing a model closes the panel.

PropTypeDefaultDescription
valuestring-The selected model's id, controlled.
defaultValuestring-The initially selected model's id.
onValueChange(value: string) => void-Called with the chosen model's id.
openboolean-The panel's open state, controlled.
defaultOpenbooleanfalseThe panel's initial open state.
onOpenChange(open: boolean) => void-Called when the panel opens or closes.

ModelSelectorTrigger

A ghost Button holding what you pass (a logo and the model's short name), with the caret added. No press scale. Forwards Button props.

PropTypeDefaultDescription
sizeButtonProps["size"]"sm"Match the bar it sits on.

ModelSelectorContent

The panel: a Command inside the Popover, with the Popover as its only surface. Forwards PopoverContent props.

PropTypeDefaultDescription
labelstring"Models"The list's accessible name.
align"start" | "center" | "end""start"Alignment to the trigger.

ModelSelectorInput

The search row. Matches the id and keywords. Forwards CommandInput props.

ModelSelectorList

The scrolling list, with the empty state. Forwards native <div> props.

PropTypeDefaultDescription
emptyReact.ReactNode"No models found."Shown when a search matches nothing.

ModelSelectorGroup

A provider's models under its name; collapses when filtered empty. Forwards CommandGroup props (heading).

ModelSelectorItem

One model. Adds the selected check in a fixed slot. Forwards CommandItem props.

PropTypeDefaultDescription
value*string-The model's id: what gets selected and searched.
keywordsstring[]-More terms to match: the display name, the provider.
disabledboolean-Shown but not choosable (no access on this plan).
Data attributeDescription
data-selectedPresent on the chosen model.

ModelSelectorLogo

A provider's mark in the row's 20px box, with a hairline over it. aria-hidden.

PropTypeDefaultDescription
srcstring-An image URL. Or pass your own SVG as children.

ModelSelectorName

The model's name with an optional second line and something after it.

PropTypeDefaultDescription
descriptionReact.ReactNode-A quiet second line: what the model is for.
addonReact.ReactNode-After the name: ModelSelectorCapabilities or a Badge.

ModelSelectorCapabilities

A run of small muted glyphs after the name. Renders a <span>.

ModelSelectorCapability

One capability: a small glyph that explains itself in a Tooltip on hover (a plain hint for the name alone, the rich tooltip with a description). The name is also screen-reader text in the row.

PropTypeDefaultDescription
label*string-What the glyph means: Vision, Reasoning.
descriptionReact.ReactNode-One line under the name in the tooltip: what it means for this model.

ModelSelectorMeta

The trailing read-out (context window, price) in tabular-nums. Renders a <span>.

FAQ#

A Select is right for three models. A real product lists a dozen across providers, each with a context window, capabilities and a price tier that decide the choice. The panel here is the DS's Command, so you get search, groups and the sliding keyboard highlight, inside a Popover with the Popover as the only surface.