Chatbot
Model Selector
A searchable command palette for selecting AI models in your chat interface.
The ModelSelector component provides a searchable command palette interface for selecting AI models. It's built on top of the cmdk library and provides a keyboard-navigable interface with search functionality.
"use client";import { ModelSelector, ModelSelectorContent, ModelSelectorEmpty, ModelSelectorGroup, ModelSelectorInput, ModelSelectorItem, ModelSelectorList, ModelSelectorLogo, ModelSelectorLogoGroup, ModelSelectorName, ModelSelectorTrigger,} from "@/components/ai-elements/model-selector";import { Button } from "@/components/ui/button";import { CheckIcon } from "lucide-react";import { useState } from "react";const models = [ { id: "gpt-4o", name: "GPT-4o", chef: "OpenAI", chefSlug: "openai", providers: ["openai", "azure"], }, { id: "gpt-4o-mini", name: "GPT-4o Mini", chef: "OpenAI", chefSlug: "openai", providers: ["openai", "azure"], }, { id: "o1", name: "o1", chef: "OpenAI", chefSlug: "openai", providers: ["openai", "azure"], }, { id: "o1-mini", name: "o1 Mini", chef: "OpenAI", chefSlug: "openai", providers: ["openai", "azure"], }, { id: "claude-opus-4-20250514", name: "Claude 4 Opus", chef: "Anthropic", chefSlug: "anthropic", providers: ["anthropic", "azure", "google-vertex", "amazon-bedrock"], }, { id: "claude-sonnet-4-20250514", name: "Claude 4 Sonnet", chef: "Anthropic", chefSlug: "anthropic", providers: ["anthropic", "azure", "google-vertex", "amazon-bedrock"], }, { id: "claude-3.5-sonnet", name: "Claude 3.5 Sonnet", chef: "Anthropic", chefSlug: "anthropic", providers: ["anthropic", "azure", "google-vertex", "amazon-bedrock"], }, { id: "claude-3.5-haiku", name: "Claude 3.5 Haiku", chef: "Anthropic", chefSlug: "anthropic", providers: ["anthropic", "azure", "google-vertex", "amazon-bedrock"], }, { id: "gemini-2.0-flash-exp", name: "Gemini 2.0 Flash", chef: "Google", chefSlug: "google", providers: ["google", "google-vertex"], }, { id: "gemini-1.5-pro", name: "Gemini 1.5 Pro", chef: "Google", chefSlug: "google", providers: ["google", "google-vertex"], }, { id: "gemini-1.5-flash", name: "Gemini 1.5 Flash", chef: "Google", chefSlug: "google", providers: ["google", "google-vertex"], }, { id: "llama-3.3-70b", name: "Llama 3.3 70B", chef: "Meta", chefSlug: "llama", providers: ["groq", "togetherai", "amazon-bedrock"], }, { id: "llama-3.1-405b", name: "Llama 3.1 405B", chef: "Meta", chefSlug: "llama", providers: ["togetherai", "amazon-bedrock"], }, { id: "llama-3.1-70b", name: "Llama 3.1 70B", chef: "Meta", chefSlug: "llama", providers: ["groq", "togetherai", "amazon-bedrock"], }, { id: "llama-3.1-8b", name: "Llama 3.1 8B", chef: "Meta", chefSlug: "llama", providers: ["groq", "togetherai"], }, { id: "deepseek-r1", name: "DeepSeek R1", chef: "DeepSeek", chefSlug: "deepseek", providers: ["deepseek", "openrouter"], }, { id: "deepseek-v3", name: "DeepSeek V3", chef: "DeepSeek", chefSlug: "deepseek", providers: ["deepseek", "openrouter"], }, { id: "deepseek-coder-v2", name: "DeepSeek Coder V2", chef: "DeepSeek", chefSlug: "deepseek", providers: ["deepseek", "openrouter"], }, { id: "mistral-large", name: "Mistral Large", chef: "Mistral AI", chefSlug: "mistral", providers: ["mistral", "azure"], }, { id: "mistral-small", name: "Mistral Small", chef: "Mistral AI", chefSlug: "mistral", providers: ["mistral", "azure"], }, { id: "codestral", name: "Codestral", chef: "Mistral AI", chefSlug: "mistral", providers: ["mistral"], }, { id: "qwen-2.5-72b", name: "Qwen 2.5 72B", chef: "Alibaba", chefSlug: "alibaba", providers: ["alibaba", "openrouter"], }, { id: "qwen-2.5-coder-32b", name: "Qwen 2.5 Coder 32B", chef: "Alibaba", chefSlug: "alibaba", providers: ["alibaba", "openrouter"], }, { id: "qwen-max", name: "Qwen Max", chef: "Alibaba", chefSlug: "alibaba", providers: ["alibaba"], }, { id: "command-r-plus", name: "Command R+", chef: "Cohere", chefSlug: "cohere", providers: ["cohere", "azure", "amazon-bedrock"], }, { id: "command-r", name: "Command R", chef: "Cohere", chefSlug: "cohere", providers: ["cohere", "azure", "amazon-bedrock"], }, { id: "grok-3", name: "Grok 3", chef: "xAI", chefSlug: "xai", providers: ["xai"], }, { id: "grok-2-1212", name: "Grok 2 1212", chef: "xAI", chefSlug: "xai", providers: ["xai"], }, { id: "grok-vision", name: "Grok Vision", chef: "xAI", chefSlug: "xai", providers: ["xai"], }, { id: "moonshot-v1-128k", name: "Moonshot v1 128K", chef: "Moonshot AI", chefSlug: "moonshotai", providers: ["moonshotai"], }, { id: "moonshot-v1-32k", name: "Moonshot v1 32K", chef: "Moonshot AI", chefSlug: "moonshotai", providers: ["moonshotai"], }, { id: "sonar-pro", name: "Sonar Pro", chef: "Perplexity", chefSlug: "perplexity", providers: ["perplexity"], }, { id: "sonar", name: "Sonar", chef: "Perplexity", chefSlug: "perplexity", providers: ["perplexity"], }, { id: "v0-chat", name: "v0 Chat", chef: "Vercel", chefSlug: "v0", providers: ["vercel"], }, { id: "nova-pro", name: "Nova Pro", chef: "Amazon", chefSlug: "amazon-bedrock", providers: ["amazon-bedrock"], }, { id: "nova-lite", name: "Nova Lite", chef: "Amazon", chefSlug: "amazon-bedrock", providers: ["amazon-bedrock"], }, { id: "nova-micro", name: "Nova Micro", chef: "Amazon", chefSlug: "amazon-bedrock", providers: ["amazon-bedrock"], },];const Example = () => { const [open, setOpen] = useState(false); const [selectedModel, setSelectedModel] = useState<string>("gpt-4o"); const selectedModelData = models.find((model) => model.id === selectedModel); // Get unique chefs in order of appearance const chefs = Array.from(new Set(models.map((model) => model.chef))); return ( <div className="flex size-full items-center justify-center p-8"> <ModelSelector onOpenChange={setOpen} open={open}> <ModelSelectorTrigger asChild> <Button className="w-[200px] justify-between" variant="outline"> {selectedModelData?.chefSlug && ( <ModelSelectorLogo provider={selectedModelData.chefSlug} /> )} {selectedModelData?.name && ( <ModelSelectorName>{selectedModelData.name}</ModelSelectorName> )} <CheckIcon className="ml-2 size-4 shrink-0 opacity-50" /> </Button> </ModelSelectorTrigger> <ModelSelectorContent> <ModelSelectorInput placeholder="Search models..." /> <ModelSelectorList> <ModelSelectorEmpty>No models found.</ModelSelectorEmpty> {chefs.map((chef) => ( <ModelSelectorGroup key={chef} heading={chef}> {models .filter((model) => model.chef === chef) .map((model) => ( <ModelSelectorItem key={model.id} onSelect={() => { setSelectedModel(model.id); setOpen(false); }} value={model.id} > <ModelSelectorLogo provider={model.chefSlug} /> <ModelSelectorName>{model.name}</ModelSelectorName> <ModelSelectorLogoGroup> {model.providers.map((provider) => ( <ModelSelectorLogo key={provider} provider={provider} /> ))} </ModelSelectorLogoGroup> {selectedModel === model.id ? ( <CheckIcon className="ml-auto size-4" /> ) : ( <div className="ml-auto size-4" /> )} </ModelSelectorItem> ))} </ModelSelectorGroup> ))} </ModelSelectorList> </ModelSelectorContent> </ModelSelector> </div> );};export default Example;Installation
npx ai-elements@latest add model-selectornpx shadcn@latest add @ai-elements/model-selectorimport { Command, CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator, CommandShortcut,} from "@repo/shadcn-ui/components/ui/command";import { Dialog, DialogContent, DialogTrigger,} from "@repo/shadcn-ui/components/ui/dialog";import { cn } from "@repo/shadcn-ui/lib/utils";import type { ComponentProps } from "react";export type ModelSelectorProps = ComponentProps<typeof Dialog>;export const ModelSelector = (props: ModelSelectorProps) => ( <Dialog {...props} />);export type ModelSelectorTriggerProps = ComponentProps<typeof DialogTrigger>;export const ModelSelectorTrigger = (props: ModelSelectorTriggerProps) => ( <DialogTrigger {...props} />);export type ModelSelectorContentProps = ComponentProps<typeof DialogContent>;export const ModelSelectorContent = ({ className, children, ...props}: ModelSelectorContentProps) => ( <DialogContent className={cn("p-0", className)} {...props}> <Command className="**:data-[slot=command-input-wrapper]:h-auto"> {children} </Command> </DialogContent>);export type ModelSelectorDialogProps = ComponentProps<typeof CommandDialog>;export const ModelSelectorDialog = (props: ModelSelectorDialogProps) => ( <CommandDialog {...props} />);export type ModelSelectorInputProps = ComponentProps<typeof CommandInput>;export const ModelSelectorInput = ({ className, ...props}: ModelSelectorInputProps) => ( <CommandInput className={cn("h-auto py-3.5", className)} {...props} />);export type ModelSelectorListProps = ComponentProps<typeof CommandList>;export const ModelSelectorList = (props: ModelSelectorListProps) => ( <CommandList {...props} />);export type ModelSelectorEmptyProps = ComponentProps<typeof CommandEmpty>;export const ModelSelectorEmpty = (props: ModelSelectorEmptyProps) => ( <CommandEmpty {...props} />);export type ModelSelectorGroupProps = ComponentProps<typeof CommandGroup>;export const ModelSelectorGroup = (props: ModelSelectorGroupProps) => ( <CommandGroup {...props} />);export type ModelSelectorItemProps = ComponentProps<typeof CommandItem>;export const ModelSelectorItem = (props: ModelSelectorItemProps) => ( <CommandItem {...props} />);export type ModelSelectorShortcutProps = ComponentProps<typeof CommandShortcut>;export const ModelSelectorShortcut = (props: ModelSelectorShortcutProps) => ( <CommandShortcut {...props} />);export type ModelSelectorSeparatorProps = ComponentProps< typeof CommandSeparator>;export const ModelSelectorSeparator = (props: ModelSelectorSeparatorProps) => ( <CommandSeparator {...props} />);export type ModelSelectorLogoProps = Omit< ComponentProps<"img">, "src" | "alt"> & { provider: | "moonshotai-cn" | "lucidquery" | "moonshotai" | "zai-coding-plan" | "alibaba" | "xai" | "vultr" | "nvidia" | "upstage" | "groq" | "github-copilot" | "mistral" | "vercel" | "nebius" | "deepseek" | "alibaba-cn" | "google-vertex-anthropic" | "venice" | "chutes" | "cortecs" | "github-models" | "togetherai" | "azure" | "baseten" | "huggingface" | "opencode" | "fastrouter" | "google" | "google-vertex" | "cloudflare-workers-ai" | "inception" | "wandb" | "openai" | "zhipuai-coding-plan" | "perplexity" | "openrouter" | "zenmux" | "v0" | "iflowcn" | "synthetic" | "deepinfra" | "zhipuai" | "submodel" | "zai" | "inference" | "requesty" | "morph" | "lmstudio" | "anthropic" | "aihubmix" | "fireworks-ai" | "modelscope" | "llama" | "scaleway" | "amazon-bedrock" | "cerebras" | (string & {});};export const ModelSelectorLogo = ({ provider, className, ...props}: ModelSelectorLogoProps) => ( <img {...props} alt={`${provider} logo`} className={cn("size-3", className)} height={12} src={`https://models.dev/logos/${provider}.svg`} width={12} />);export type ModelSelectorLogoGroupProps = ComponentProps<"div">;export const ModelSelectorLogoGroup = ({ className, ...props}: ModelSelectorLogoGroupProps) => ( <div className={cn( "-space-x-1 flex shrink-0 items-center [&>img]:rounded-full [&>img]:bg-background [&>img]:p-px [&>img]:ring-1 [&>img]:ring-border", className )} {...props} />);export type ModelSelectorNameProps = ComponentProps<"span">;export const ModelSelectorName = ({ className, ...props}: ModelSelectorNameProps) => ( <span className={cn("flex-1 truncate text-left", className)} {...props} />);Features
- Searchable interface with keyboard navigation
- Fuzzy search filtering across model names
- Grouped model organization by provider
- Keyboard shortcuts support
- Empty state handling
- Customizable styling with Tailwind CSS
- Built on cmdk for excellent accessibility
- Support for both inline and dialog modes
- TypeScript support with proper type definitions
Props
<ModelSelector />
Prop
Type
<ModelSelectorTrigger />
Prop
Type
<ModelSelectorContent />
Prop
Type
<ModelSelectorDialog />
Prop
Type
<ModelSelectorInput />
Prop
Type
<ModelSelectorList />
Prop
Type
<ModelSelectorEmpty />
Prop
Type
<ModelSelectorGroup />
Prop
Type
<ModelSelectorItem />
Prop
Type
<ModelSelectorShortcut />
Prop
Type
<ModelSelectorSeparator />
Prop
Type
<ModelSelectorLogo />
Prop
Type
<ModelSelectorLogoGroup />
Prop
Type
<ModelSelectorName />
Prop
Type