--- title: Model Selector description: A dropdown for selecting an AI model with radio groups, sub-menus, and custom items --- import ModelSelectorDefault from "@/components/nexus-ui/examples/model-selector/default"; import ModelSelectorBasic from "@/components/nexus-ui/examples/model-selector/basic"; import ModelSelectorWithSub from "@/components/nexus-ui/examples/model-selector/with-sub"; import ModelSelectorWithItems from "@/components/nexus-ui/examples/model-selector/with-items"; import ModelSelectorWithCheckbox from "@/components/nexus-ui/examples/model-selector/with-checkbox"; import ModelSelectorTriggerVariants from "@/components/nexus-ui/examples/model-selector/trigger-variants"; import ModelSelectorWithPromptInput from "@/components/nexus-ui/examples/model-selector/with-prompt-input"; import ModelSelectorCustomTrigger from "@/components/nexus-ui/examples/model-selector/custom-trigger"; import ModelSelectorWithSearch from "@/components/nexus-ui/examples/model-selector/with-search"; A composable dropdown for selecting AI models. Built on [Radix UI Dropdown Menu](https://www.radix-ui.com/primitives/docs/components/dropdown-menu), it supports radio groups for single selection, sub-menus for nested options, plain items for toggles or actions, and separators for grouping. The trigger displays the selected model's icon and title when you pass an `items` array. ## Installation ```bash npx shadcn@latest add @nexus-ui/model-selector ``` ```bash pnpm dlx shadcn@latest add @nexus-ui/model-selector ``` ```bash yarn dlx shadcn@latest add @nexus-ui/model-selector ``` ```bash bunx shadcn@latest add @nexus-ui/model-selector ```

Copy and paste the following code into your project.

Update the import paths to match your project setup.

## Usage ```tsx keepBackground import { ModelSelector, ModelSelectorTrigger, ModelSelectorContent, ModelSelectorGroup, ModelSelectorLabel, ModelSelectorRadioGroup, ModelSelectorRadioItem, } from "@/components/nexus-ui/model-selector"; ``` ```tsx keepBackground noCollapse Select model {models.map((m) => ( ))} ``` ## Examples ### Basic A minimal model selector with radio items. No icons, descriptions, or labels. ### Trigger Variants The trigger supports three variants: `filled` (default), `outline`, and `ghost`. Use `outline` or `ghost` when placing the model selector inside a [Prompt Input](/docs/components/prompt-input) or other dense UI. ### Custom Trigger Pass custom `children` to `ModelSelectorTrigger` to override the default icon-and-title layout. You control the content entirely—use your own copy, icons, and layout. ### With Sub-menus Use `ModelSelectorSub`, `ModelSelectorSubTrigger`, `ModelSelectorPortal`, and `ModelSelectorSubContent` for nested options like provider-specific model groups. ### With Checkbox Use `ModelSelectorCheckboxItem` alone for multi-select. Each item is a checkbox—toggle models on or off. Use a custom trigger to show the selection (e.g. "Select models", "GPT-4", or "3 models"). ### With Prompt Input Place the model selector in a `PromptInputActionGroup` alongside attach, image, mic, and send buttons. ### With Search Place `ModelSelectorSearch` first in `ModelSelectorContent`; the root owns the query and clears it when the menu closes (`onOpenChange` still runs if you pass it). Radio items filter on `value`, `title`, and `description`; checkbox items on `title` and `description` only; plain `ModelSelectorItem` rows are not filtered. Optional search `onChange` runs after internal updates. `ModelSelectorEmpty` needs root `items` to show no-match copy (default: No models found). ### With Items and Separators Use `ModelSelectorItem` for non-selection actions (e.g. extended thinking toggle) and `ModelSelectorSeparator` to divide sections. ## Vercel AI SDK Integration Use the Model Selector with the [Vercel AI SDK](https://sdk.vercel.ai) to let users pick which model powers the chat. Pass the selected model to your API via `prepareSendMessagesRequest`.

Install the AI SDK

```bash npm install ai @ai-sdk/react @ai-sdk/openai ```

Create your chat API route

Create a route that reads `model` from the request body: ```ts title="app/api/chat/route.ts" import { convertToModelMessages, streamText, UIMessage } from "ai"; import { openai } from "@ai-sdk/openai"; export async function POST(req: Request) { const { messages, model = "gpt-4o-mini" }: { messages: UIMessage[]; model?: string } = await req.json(); const result = streamText({ model: openai(model), system: "You are a helpful assistant.", messages: await convertToModelMessages(messages), }); return result.toUIMessageStreamResponse(); } ```

Wire Model Selector + Prompt Input to `useChat`

```tsx "use client"; import { useState } from "react"; import { useChat } from "@ai-sdk/react"; import { DefaultChatTransport } from "ai"; import { Button } from "@/components/ui/button"; import PromptInput, { PromptInputActions, PromptInputAction, PromptInputActionGroup, PromptInputTextarea, } from "@/components/nexus-ui/prompt-input"; import { ModelSelector, ModelSelectorContent, ModelSelectorGroup, ModelSelectorLabel, ModelSelectorRadioGroup, ModelSelectorRadioItem, ModelSelectorTrigger, } from "@/components/nexus-ui/model-selector"; import ChatgptIcon from "@/components/svgs/chatgpt"; import { ArrowUp02Icon, PlusSignIcon, SquareIcon, } from "@hugeicons/core-free-icons"; import { HugeiconsIcon } from "@hugeicons/react"; const models = [ { value: "gpt-4", icon: ChatgptIcon, title: "GPT-4", description: "Most capable" }, { value: "gpt-4o-mini", icon: ChatgptIcon, title: "GPT-4o Mini", description: "Fast" }, { value: "gpt-4o", icon: ChatgptIcon, title: "GPT-4o", description: "Multimodal" }, ]; export default function ChatWithModelSelector() { const [model, setModel] = useState("gpt-4o-mini"); const { sendMessage, status } = useChat({ transport: new DefaultChatTransport({ api: "/api/chat", prepareSendMessagesRequest: ({ body }) => ({ body: { ...body, model }, }), }), }); const [input, setInput] = useState(""); const isLoading = status !== "ready"; const handleSubmit = (value?: string) => { const text = (value ?? input).trim(); if (text) { sendMessage({ text }); setInput(""); } }; return (
{ e.preventDefault(); handleSubmit(); }} className="w-full"> setInput(e.target.value)} placeholder="Ask anything..." disabled={isLoading} /> Select model {models.map((m) => ( ))}
); } ```
For multi-provider support (OpenAI, Anthropic, etc.), add `@ai-sdk/anthropic` and similar, then map the model value to the correct provider in your API route. ## API Reference The Model Selector primitives extend [Radix UI Dropdown Menu](https://www.radix-ui.com/primitives/docs/components/dropdown-menu). For props like `open`, `onOpenChange`, `modal`, `dir`, etc., refer to the Radix docs. Below are the props specific to the Model Selector. ### ModelSelector Root component. Provides `value`, `onValueChange`, and `items` to children via context. void", description: "Called when the selection changes.", }, onOpenChange: { type: "(open: boolean) => void", description: "Forwarded from the Radix menu root. The root also clears the internal search query when the menu closes.", }, items: { type: "Array<{ value: string; icon?: React.ComponentType<{ className?: string }>; title: string; description?: string }>", description: "Optional. Used by ModelSelectorTrigger to display the selected model's icon and title. Omit for custom trigger content.", }, }} /> ### ModelSelectorTrigger The button that opens the dropdown. Shows selected model (icon + title) when `items` is provided, or custom `children`. Supports `asChild` to merge into a child element (e.g. Button). ### ModelSelectorSearch A search input bound to the root filter query; radio and checkbox items hide when they do not match. Place at the top of `ModelSelectorContent`. Accepts standard `` props. ### ModelSelectorEmpty Shown when the query is non-empty, `items` is set on the root, and none of those entries match the filter. Otherwise hidden. Default copy: No models found. Accepts standard `
` props. ### ModelSelectorItem A plain menu item (no selection state). Use for toggles, links, or actions. ### ModelSelectorItemTitle A paragraph for the item title. Used internally by CheckboxItem and RadioItem when `title` is provided. Accepts standard `p` element props. ### ModelSelectorItemDescription A paragraph for the item description. Used internally by CheckboxItem and RadioItem when `description` is provided. Accepts standard `p` element props. ### ModelSelectorItemIcon A span wrapper for the item icon. Used internally by CheckboxItem and RadioItem when `icon` is provided. Accepts standard `span` element props. ### ModelSelectorItemIndicator A span that wraps selection indicators. Renders children inside Radix `ItemIndicator` by default. Use `wrapWithItemIndicator={false}` for always-visible content (e.g. LockIcon when disabled). ### ModelSelectorCheckboxItem A checkbox-style item for multi-select. Shows a check indicator when `checked` is true, or a lock icon when `disabled`. ", description: "Optional icon component for the item.", }, indicator: { type: "React.ReactNode", default: "CheckIcon", description: "Custom indicator to show when selected. Renders inside ItemIndicator.", }, title: { type: "string", description: "Optional title. Override with children for full control.", }, description: { type: "string", description: "Optional description shown below the title.", }, }} /> ### ModelSelectorRadioItem A radio-style item for single selection. Shows a check when selected, or a lock icon when `disabled`. ", description: "Optional icon component for the item.", }, indicator: { type: "React.ReactNode", default: "CheckIcon", description: "Custom indicator to show when selected. Renders inside ItemIndicator.", }, title: { type: "string", description: "Optional title. Override with children for full control.", }, description: { type: "string", description: "Optional description shown below the title.", }, }} /> ### Other primitives `ModelSelectorContent`, `ModelSelectorPortal`, `ModelSelectorGroup`, `ModelSelectorLabel`, `ModelSelectorRadioGroup`, `ModelSelectorSeparator`, `ModelSelectorSub`, `ModelSelectorSubTrigger`, and `ModelSelectorSubContent` accept the same props as their Radix Dropdown Menu counterparts. See the [Radix Dropdown Menu documentation](https://www.radix-ui.com/primitives/docs/components/dropdown-menu) for details. `ModelSelectorItemTitle`, `ModelSelectorItemDescription`, `ModelSelectorItemIcon`, and `ModelSelectorItemIndicator` are low-level building blocks used by CheckboxItem and RadioItem. Use them when composing custom item content with `children`.