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-selector
npx shadcn@latest add @ai-elements/model-selector
import {  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