Elements

82 / 122 · Composer

Model picker

The full list rather than the rail: grouped by family, priced, with what each one can do.

Anthropic
Local

Installation

npx shadcn@latest add "@assistant-ui/elements-model-picker"

Usage

import { ModelPicker } from "@/components/elements/model-picker";

<ModelPicker models={models} selectedId={selectedId} onSelect={setSelectedId} />

Props

ModelPicker

models*PickableModel[]Every model on offer. Families are derived from the entries and keep their first-seen order.
selectedId*stringWhich model is active.
onSelect(id: string) => voidCalled when a model is picked.
classNamestringExtra classes merged onto the root.

PickableModel

id*stringStable identity, compared against selectedId and reported by onSelect.
name*stringDisplay name of the model.
family*stringGroup heading the model sits under.
context*stringWindow size, pre-formatted.
price*stringCost, pre-formatted. The element never does currency math.
capabilities*string[]Short capability chips: vision, tools, thinking.

Source

model-picker.tsx
"use client";

import { CheckIcon } from "lucide-react";
import { cn } from "@/lib/utils";
import { field, mono, paper } from "./surfaces";

export interface PickableModel {
  id: string;
  name: string;
  family: string;
  context: string;
  price: string;
  capabilities: readonly string[];
}

export function ModelPicker({
  models,
  selectedId,
  onSelect,
  className,
}: {
  models: readonly PickableModel[];
  selectedId: string;
  onSelect?: (id: string) => void;
  className?: string;
}) {
  const families = [...new Set(models.map((model) => model.family))];

  return (
    <div
      className={cn(
        paper,
        "flex w-full max-w-sm flex-col gap-1 rounded-2xl p-2",
        className,
      )}
    >
      {families.map((family) => (
        <div key={family} className="flex flex-col">
          <span className={cn(mono, "text-foreground/30 px-2 pt-2 pb-1")}>
            {family}
          </span>
          {models
            .filter((model) => model.family === family)
            .map((model) => {
              const selected = model.id === selectedId;
              return (
                <button
                  key={model.id}
                  type="button"
                  aria-pressed={selected}
                  onClick={() => onSelect?.(model.id)}
                  className={cn(
                    "flex items-center gap-2.5 rounded-xl px-2 py-2 text-start transition-colors",
                    selected
                      ? "bg-foreground/[0.06]"
                      : "hover:bg-foreground/[0.035]",
                  )}
                >
                  <span className="flex size-3.5 shrink-0 items-center justify-center">
                    {selected && (
                      <CheckIcon className="fade-in zoom-in-90 animate-in text-foreground/70 size-3.5 duration-200" />
                    )}
                  </span>

                  <span className="flex min-w-0 flex-1 flex-col gap-1">
                    <span className="truncate text-[13.5px]">{model.name}</span>
                    <span className="flex flex-wrap gap-1">
                      {model.capabilities.map((capability) => (
                        <span
                          key={capability}
                          className={cn(
                            field,
                            mono,
                            "text-foreground/45 rounded px-1 py-px",
                          )}
                        >
                          {capability}
                        </span>
                      ))}
                    </span>
                  </span>

                  <span className="flex shrink-0 flex-col items-end gap-1">
                    <span
                      className={cn(mono, "text-foreground/35 tabular-nums")}
                    >
                      {model.context}
                    </span>
                    <span
                      className={cn(mono, "text-foreground/25 tabular-nums")}
                    >
                      {model.price}
                    </span>
                  </span>
                </button>
              );
            })}
        </div>
      ))}
    </div>
  );
}