<ModelSelector />
A cross-platform model picker for AI interfaces, with built-in provider logos, model labels, and compact trigger patterns for web and mobile.
<ModelSelector /> gives you a polished way to let users switch models without rebuilding the same provider-and-model UI every time. It wraps the lower-level select primitives in a shape that already feels right for AI products.
Web
The web version is built for compact dropdown usage in chat toolbars, composer controls, and settings surfaces. It keeps the trigger small and lets the selected model label carry most of the visual weight.

Mobile
The mobile version follows the same mental model, but it gives the trigger a little more visual context by including the provider or model logo directly inside the trigger. That makes model switching easier to scan in touch-first layouts.

Why it matters
Model choice is often one of the most important controls in an AI product, but it can also become visually messy very quickly. This component helps you present that choice in a way that feels intentional instead of improvised.
Designed for AI workflows
The component already understands provider logos, model names, and the kind of compact trigger most chat products need.
Reusable beyond the dropdown
The logo and name helpers are useful on their own in places like usage panels, model badges, and message metadata.
Consistent across surfaces
Whether the selector appears in a composer, a settings area, or a context panel, it keeps the visual language of model choice consistent.
Building blocks
<ModelSelector /> is best thought of as a small family of parts rather than one monolithic control. You can use the default composition, or pull out the logo and name helpers when you need them elsewhere.
The exported parts are:
<ModelSelector /><ModelSelectorTrigger /><ModelSelectorContent /><ModelSelectorItem /><ModelSelectorLogo /><ModelSelectorName />
Basic usage
The usual pattern is a root selector with a trigger and a list of items. The exact select state wiring depends on your app, but the visual composition is the same across platforms.
import {
ModelSelector,
ModelSelectorContent,
ModelSelectorItem,
ModelSelectorTrigger,
} from "@workspace/ui-web/ai-elements/model-selector";
export function ChatModelSelector() {
return (
<ModelSelector value="gpt-4.1-mini">
<ModelSelectorTrigger />
<ModelSelectorContent>
<ModelSelectorItem value="gpt-4.1-mini">GPT-4.1 Mini</ModelSelectorItem>
<ModelSelectorItem value="claude-4-sonnet">
Claude 4 Sonnet
</ModelSelectorItem>
<ModelSelectorItem value="gemini-2.5-flash">
Gemini 2.5 Flash
</ModelSelectorItem>
</ModelSelectorContent>
</ModelSelector>
);
}import {
ModelSelector,
ModelSelectorContent,
ModelSelectorItem,
ModelSelectorTrigger,
} from "@workspace/ui-mobile/ai-elements/model-selector";
export function ChatModelSelector() {
return (
<ModelSelector value="gpt-4.1-mini">
<ModelSelectorTrigger provider="openai" model="gpt-4.1-mini" />
<ModelSelectorContent>
<ModelSelectorItem value="gpt-4.1-mini">GPT-4.1 Mini</ModelSelectorItem>
<ModelSelectorItem value="claude-4-sonnet">
Claude 4 Sonnet
</ModelSelectorItem>
<ModelSelectorItem value="gemini-2.5-flash">
Gemini 2.5 Flash
</ModelSelectorItem>
</ModelSelectorContent>
</ModelSelector>
);
}Logo and name helpers
One of the most useful details in this component family is that the branding logic is reusable. You do not need to duplicate provider-logo matching in other parts of the interface.
import {
ModelSelectorLogo,
ModelSelectorName,
} from "@workspace/ui-web/ai-elements/model-selector";
export function ModelMeta() {
return (
<div className="flex items-center gap-2">
<ModelSelectorLogo provider="anthropic" model="claude-4-sonnet" />
<ModelSelectorName>Claude 4 Sonnet</ModelSelectorName>
</div>
);
}import {
ModelSelectorLogo,
ModelSelectorName,
} from "@workspace/ui-mobile/ai-elements/model-selector";
export function ModelMeta() {
return (
<View className="flex-row items-center gap-2">
<ModelSelectorLogo provider="anthropic" model="claude-4-sonnet" />
<ModelSelectorName>Claude 4 Sonnet</ModelSelectorName>
</View>
);
}The helpers first try to match model-specific icons for names like claude, gemini, grok, or nano-banana. If no model-specific icon matches, they fall back to the provider icon, and then finally to an external logo from models.dev.
Platform differences
The two versions stay close in spirit, but there are a few differences worth knowing when you design around them.
| Area | Web | Mobile |
|---|---|---|
| Trigger | Compact text-first trigger | Trigger includes logo by default |
| Logo fallback | img fallback from models.dev | expo-image fallback from models.dev |
| Name helper | span-based text helper | native Text-based helper |
| Visual feel | tighter desktop toolbar fit | easier scanning in touch layouts |
What to customize
Most customization happens through composition and styling rather than through a long prop list. In practice, the main knobs are:
- the selected value and state wiring from the underlying select primitive
classNameon trigger, item, logo, or name helpers- the
providerandmodelvalues used to resolve the right logo sizeon the mobile logo helper when you need a larger or smaller icon
That makes the component easy to adapt without turning it into a configuration-heavy abstraction.
Related components
<ModelSelector /> tends to live near other model-aware pieces of the UI. These are the most natural companion pages in this docs set.
How is this guide?
Last updated on
<Message />
A composable AI message UI for web and mobile, with content, actions, markdown response rendering, and branch navigation for alternate generations.
<PromptInput />
A composable AI prompt input for web and mobile, with textarea, submit controls, tools, action menus, attachments, and provider-driven state management.