Elements

98 / 122 · Thread

Settings

Model, system prompt, temperature, and what the assistant is allowed to do.

model
system prompt
temperature0.7
Tool useLet the model call your tools
MemoryRemember facts between threads

Installation

npx shadcn@latest add "@assistant-ui/elements-settings-panel"

Usage

import { SettingsPanel } from "@/components/elements/settings-panel";

<SettingsPanel
  model={model}
  models={models}
  systemPrompt={prompt}
  temperature={0.7}
  toggles={toggles}
  onModelChange={setModel}
/>

Props

model*stringSelected model.
models*string[]Models offered in the segmented control. Keep it to a few; more wants the model picker.
systemPrompt*stringCurrent system prompt.
temperature*numberCurrent temperature, 0 to 2.
toggles*SettingToggle[]Capability switches with a line each explaining what they turn on.
onModelChange(model: string) => voidCalled when the model changes.
onSystemPromptChange(prompt: string) => voidCalled as the prompt is edited.
onTemperatureChange(temperature: number) => voidCalled as the slider moves.
onToggle(key: string) => voidCalled with the switched capability's key.
classNamestringExtra classes merged onto the root.

Source

settings-panel.tsx
"use client";

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

export interface SettingToggle {
  key: string;
  label: string;
  detail: string;
  on: boolean;
}

export function SettingsPanel({
  model,
  models,
  systemPrompt,
  temperature,
  toggles,
  onModelChange,
  onSystemPromptChange,
  onTemperatureChange,
  onToggle,
  className,
}: {
  model: string;
  models: readonly string[];
  systemPrompt: string;
  temperature: number;
  toggles: readonly SettingToggle[];
  onModelChange?: (model: string) => void;
  onSystemPromptChange?: (prompt: string) => void;
  onTemperatureChange?: (temperature: number) => void;
  onToggle?: (key: string) => void;
  className?: string;
}) {
  return (
    <div
      className={cn(
        paper,
        "flex w-full max-w-sm flex-col gap-4 rounded-[20px] p-4",
        className,
      )}
    >
      <div className="flex flex-col gap-1.5">
        <span className={cn(mono, "text-foreground/30")}>model</span>
        <div className={cn(field, "flex gap-0.5 rounded-full p-0.5")}>
          {models.map((option) => (
            <button
              key={option}
              type="button"
              aria-pressed={option === model}
              onClick={() => onModelChange?.(option)}
              className={cn(
                "flex-1 rounded-full py-1 text-xs font-medium transition-[background-color,color,scale] duration-150 active:scale-[0.97]",
                option === model
                  ? "bg-background text-foreground/90 shadow-[0_1px_2px_rgba(0,0,0,0.06)]"
                  : "text-foreground/45 hover:text-foreground/70",
              )}
            >
              {option}
            </button>
          ))}
        </div>
      </div>

      <div className="flex flex-col gap-1.5">
        <span className={cn(mono, "text-foreground/30")}>system prompt</span>
        <textarea
          value={systemPrompt}
          onChange={(event) => onSystemPromptChange?.(event.target.value)}
          rows={3}
          aria-label="System prompt"
          className={cn(
            field,
            "text-foreground/80 focus-visible:ring-foreground/20 resize-none rounded-xl px-3 py-2 text-xs leading-relaxed outline-none focus-visible:ring-2",
          )}
        />
      </div>

      <div className="flex flex-col gap-1.5">
        <span className="flex items-baseline justify-between">
          <span className={cn(mono, "text-foreground/30")}>temperature</span>
          <span className={cn(mono, "text-foreground/55 tabular-nums")}>
            {Math.min(2, Math.max(0, temperature)).toFixed(1)}
          </span>
        </span>
        <input
          type="range"
          min={0}
          max={2}
          step={0.1}
          value={Math.min(2, Math.max(0, temperature))}
          aria-label="Temperature"
          onChange={(event) =>
            onTemperatureChange?.(Number(event.target.value))
          }
          className="accent-foreground/80 h-1 w-full cursor-pointer"
        />
      </div>

      <div className="flex flex-col gap-2.5">
        {toggles.map((toggle) => (
          <div key={toggle.key} className="flex items-center gap-3">
            <span className="flex min-w-0 flex-1 flex-col">
              <span className="truncate text-[13px]">{toggle.label}</span>
              <span className="text-foreground/35 truncate text-xs">
                {toggle.detail}
              </span>
            </span>
            <button
              type="button"
              role="switch"
              aria-checked={toggle.on}
              aria-label={toggle.label}
              onClick={() => onToggle?.(toggle.key)}
              className={cn(
                "flex h-5 w-9 shrink-0 items-center rounded-full p-0.5 transition-colors duration-200",
                toggle.on ? "bg-foreground/80" : "bg-foreground/15",
              )}
            >
              <span
                className={cn(
                  "bg-background size-4 rounded-full transition-transform duration-200 motion-reduce:transition-none",
                  toggle.on && "translate-x-4",
                )}
              />
            </button>
          </div>
        ))}
      </div>
    </div>
  );
}