# Model & Memory Preferences

> A preferences screen that picks the model, reasoning level and tools, and manages persistent memory.

A shadcn/ui block from the [@scrimui](https://scrimui.dev) registry, listed on [shadcn.exchange](https://shadcn.exchange/r/scrimui/model-preferences).

- Page, with a live preview: https://shadcn.exchange/r/scrimui/model-preferences
- Screenshot: https://abzjhwgwpdpz7ua5.public.blob.vercel-storage.com/shots/scrimui/model-preferences-light-99227d0187911b0b.png
- Registry item: https://scrimui.dev/r/model-preferences.json
- Categories: pattern
- Listed: 2026-10-13, source last changed 2026-10-13

## Install

```bash
npx shadcn@latest add https://scrimui.dev/r/model-preferences.json
```

Run it in a project that has shadcn/ui set up. It adds the files below and installs what they depend on.

## Dependencies

- Registry items: `https://scrimui.dev/r/model-selector.json`, `https://scrimui.dev/r/reasoning-level.json`, `https://scrimui.dev/r/tool-toggle.json`, `https://scrimui.dev/r/memory-list.json`, `badge`, `card`

## Notes from the registry

https://scrimui.dev/patterns/model-preferences

## Files

### `src/showcase/patterns/model-preferences/model-preferences.tsx`

```tsx
"use client";

import { Card } from "@/components/ui/card";
import { Badge } from "@/components/ui/badge";
import * as React from "react";
import { ModelSelector, type ModelOption } from "@/components/ui/model-selector";
import {
  ReasoningLevel,
  type ReasoningLevel as ReasoningLevelValue,
} from "@/components/ui/reasoning-level";
import { ToolToggle, type ToolSetting } from "@/components/ui/tool-toggle";
import { MemoryList, type MemoryItem } from "@/components/ui/memory-list";

/* ------------------------------------------------------------------ */
/* Data                                                                */
/* ------------------------------------------------------------------ */

const MODELS: ModelOption[] = [
  { id: "sonnet", name: "Claude Sonnet 5", hint: "Balanced speed and quality", badges: ["Default"], icon: <ClaudeMark /> },
  { id: "opus", name: "Claude Opus 5", hint: "Best for hard problems", badges: ["Deep thinking"], icon: <ClaudeMark /> },
  { id: "haiku", name: "Claude Haiku 4.5", hint: "Fastest responses", badges: ["Fast"], icon: <ClaudeMark /> },
];

function initTools(): ToolSetting[] {
  return [
    {
      id: "search",
      name: "Web search",
      description: "Find current information online",
      enabled: true,
      icon: <SearchIcon />,
    },
    {
      id: "code",
      name: "Code execution",
      description: "Run code to verify answers",
      enabled: true,
      icon: <CodeIcon />,
    },
    {
      id: "files",
      name: "File access",
      description: "Read and edit attached files",
      enabled: false,
      icon: <FileIcon />,
    },
    {
      id: "browser",
      name: "Browser",
      description: "Open pages from the conversation",
      enabled: false,
      icon: <GlobeIcon />,
    },
  ];
}

const INITIAL_MEMORIES: MemoryItem[] = [
  {
    id: "m1",
    text: "Builds AI chat interfaces in TypeScript with Tailwind and zero UI dependencies.",
    updatedAt: "2d ago",
  },
  { id: "m2", text: "Prefers zinc-based palettes and compact, dependency-free components.", updatedAt: "5d ago" },
  { id: "m3", text: "Shipping a voice assistant pattern this week.", updatedAt: "Today" },
];

/* ------------------------------------------------------------------ */
/* Icons                                                               */
/* ------------------------------------------------------------------ */

/**
 * The provider's mark, inlined so this pattern stays dependency-free. Swap it
 * for whichever providers your own model list uses.
 */
function ClaudeMark() {
  return (
    <svg viewBox="0 0 24 24" width="13" height="13" aria-hidden className="shrink-0 fill-[#d97757]">
      <path d="M17.3041 3.541h-3.6718l6.696 16.918H24Zm-10.6082 0L0 20.459h3.7442l1.3693-3.5527h7.0052l1.3693 3.5528h3.7442L10.5363 3.5409Zm-.3712 10.2232 2.2914-5.9456 2.2914 5.9456Z" />
    </svg>
  );
}

function SearchIcon() {
  return (
    <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" width="15" height="15">
      <circle cx="11" cy="11" r="7" />
      <path d="m21 21-4.3-4.3" />
    </svg>
  );
}

function CodeIcon() {
  return (
    <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" width="15" height="15">
      <path d="m8 7-5 5 5 5" />
      <path d="m16 7 5 5-5 5" />
    </svg>
  );
}

function FileIcon() {
  return (
    <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" width="15" height="15">
      <path d="M14.5 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7.5L14.5 2Z" />
      <path d="M14 2v6h6" />
    </svg>
  );
}

function GlobeIcon() {
  return (
    <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" width="15" height="15">
      <circle cx="12" cy="12" r="10" />
      <path d="M2 12h20M12 2a15.3 15.3 0 0 1 0 20 15.3 15.3 0 0 1 0-20Z" />
    </svg>
  );
}

/* ------------------------------------------------------------------ */
/* ModelPreferencesPattern                                             */
/* ------------------------------------------------------------------ */

export function ModelPreferencesPattern() {
  const [model, setModel] = React.useState("sonnet");
  const [level, setLevel] = React.useState<ReasoningLevelValue>("balanced");
  const [tools, setTools] = React.useState<ToolSetting[]>(initTools);
  const [memories, setMemories] = React.useState<MemoryItem[]>(INITIAL_MEMORIES);
  const idRef = React.useRef(4);

  function toggleTool(id: string, enabled: boolean) {
    setTools((ts) => ts.map((t) => (t.id === id ? { ...t, enabled } : t)));
  }

  function addMemory(text: string) {
    setMemories((m) => [...m, { id: `m${idRef.current++}`, text, updatedAt: "Just now" }]);
  }

  function forgetMemory(id: string) {
    setMemories((m) => m.filter((item) => item.id !== id));
  }

  return (
    <Card className="gap-0 py-0 flex h-[560px] flex-col overflow-hidden rounded-xl border border-border bg-card">
      {/* Header */}
      <div className="flex items-center justify-between border-b border-border px-5 py-3.5">
        <div>
          <p className="text-sm font-semibold text-foreground">
            Model &amp; Memory Preferences
          </p>
          <p className="mt-0.5 text-xs text-muted-foreground">
            How the assistant thinks, which tools it may use, and what it remembers.
          </p>
        </div>
        <Badge variant="secondary" className="inline-flex items-center gap-1.5 rounded-full bg-emerald-100 px-2 py-0.5 text-xs font-medium text-emerald-700 dark:bg-emerald-900/40 dark:text-emerald-400">
          <span className="h-1.5 w-1.5 rounded-full bg-current" />
          Saved
        </Badge>
      </div>

      <div className="flex min-h-0 flex-1">
        {/* Settings */}
        <div className="flex-1 space-y-6 overflow-y-auto p-5">
          <section className="space-y-2">
            <div>
              <p className="text-sm font-medium text-foreground">Default model</p>
              <p className="mt-0.5 text-xs text-muted-foreground">
                Used for new conversations; you can still switch per message.
              </p>
            </div>
            <ModelSelector options={MODELS} value={model} onSelect={setModel} />
          </section>

          <ReasoningLevel value={level} onChange={setLevel} />

          <ToolToggle
            tools={tools}
            onToggle={toggleTool}
            title="Allowed tools"
            description="What the assistant may do on your behalf"
          />
        </div>

        {/* Memory */}
        <div className="hidden w-80 shrink-0 flex-col overflow-y-auto border-l border-border p-5 md:flex">
          <MemoryList
            items={memories}
            onAdd={addMemory}
            onForget={forgetMemory}
            description="Facts the assistant keeps across conversations"
          />
          <p className="mt-3 text-xs leading-5 text-muted-foreground">
            Memories are included with each message so the assistant stays consistent. Remove
            anything you do not want stored.
          </p>
        </div>
      </div>
    </Card>
  );
}
```
