Skip to content
Scrim UI

Tool Toggle UI

Switches for the tools a model may use — web search, code execution, file access — with unmistakable on and off states.

tooltoggleswitchsettings

Preview

Tools

What the assistant is allowed to use

  • Web search

    Find current information online

  • Code execution

    Run code to verify answers

  • File access

    Read and edit attached files

Presets

Props

Also takes onToggle.

Agent promptClaude Code · Cursor · any agent
Add the Tool Toggle UI component from the Scrim UI registry to this project and use it in the configuration described below.

Tool Toggle UI — Switches for the tools a model may use — web search, code execution, file access — with unmistakable on and off states.

## 1. Install

```bash
npx shadcn@latest add @scrimui/tool-toggle
```

This writes a single file to `components/ui/tool-toggle.tsx`. It is plain React + Tailwind with no runtime dependencies — no Radix, no CVA, nothing to add to package.json. If this project does not use the shadcn CLI, copy the source from https://scrimui.dev/components/tool-toggle to the same path by hand; nothing in the file depends on shadcn.

## 2. Use it

```tsx
import { ToolToggle } from "@/components/ui/tool-toggle";

const TOOLS = [
  { id: "t1", name: "Web search", description: "Find current information online", enabled: true },
  { id: "t2", name: "Code execution", description: "Run code to verify answers", enabled: true },
  { id: "t3", name: "File access", description: "Read and edit attached files", enabled: false },
];

<ToolToggle
  title="Tools"
  description="What the assistant is allowed to use"
  tools={TOOLS}
  onToggle={() => {}}
/>
```

## 3. The configuration that matters

This is the component's "Default" state — Live switches for the assistant's tools — flip one and the state updates.

Every prop is at its default value. Keep the call site minimal — do not write out default values.

## 4. Wire the handlers

`onToggle` is a stub in the snippet above (`() => {}`). Connect it to this project's own state and data layer instead of shipping the empty function.

Reference: https://scrimui.dev/components/tool-toggle

Follows the props below — change a control and the prompt changes with it, so an agent reproduces that configuration instead of the defaults.

Install

npx shadcn@latest add @scrimui/tool-toggle

Component source

React + Tailwind component using shadcn/ui (button, card). The install command adds these dependencies automatically.

When copying source manually, install the required primitives first:

npx shadcn@latest add button card
tool-toggle.tsx
"use client";

import { Card } from "@/components/ui/card";
import { Button } from "@/components/ui/button";
import * as React from "react";

/* ------------------------------------------------------------------ */
/* Types                                                               */
/* ------------------------------------------------------------------ */

export type ToolSetting = {
  id: string;
  name: string;
  description: string;
  enabled: boolean;
  disabled?: boolean;
  icon?: React.ReactNode;
};

export type ToolToggleProps = {
  tools: ToolSetting[];
  title?: string;
  description?: string;
  onToggle?: (id: string, enabled: boolean) => void;
  className?: string;
};

/* ------------------------------------------------------------------ */
/* ToolToggle                                                          */
/* ------------------------------------------------------------------ */

export function ToolToggle({
  tools,
  title = "Tools",
  description = "What the assistant is allowed to use",
  onToggle,
  className = "",
}: ToolToggleProps) {
  /* Prefix for the per-row label/description ids the switches point at. */
  const uid = React.useId();

  return (
    <Card className={`gap-0 py-0 rounded-xl border border-border bg-card ${className}`}>
      <div className="border-b border-border px-4 py-3">
        <p className="text-sm font-medium text-foreground">{title}</p>
        <p className="mt-0.5 text-xs text-muted-foreground">{description}</p>
      </div>

      <ul className="divide-y divide-border">
        {tools.map((tool) => (
          <li key={tool.id} className="flex items-center gap-3 px-4 py-3">
            {tool.icon && (
              <span className="flex h-8 w-8 shrink-0 items-center justify-center rounded-lg bg-muted text-muted-foreground">
                {tool.icon}
              </span>
            )}
            <div className="min-w-0 flex-1">
              <p
                id={`${uid}-${tool.id}-name`}
                className="text-sm font-medium text-foreground"
              >
                {tool.name}
              </p>
              <p
                id={`${uid}-${tool.id}-desc`}
                className="mt-0.5 text-xs text-muted-foreground"
              >
                {tool.description}
              </p>
            </div>
            {/* The switch is a bare coloured pill, so without these it has no
                accessible name at all — a screen reader announces "switch, on"
                with no clue which tool it governs. Pointing at the visible
                label rather than duplicating the string in an aria-label keeps
                the two from drifting apart. */}
            <Button variant="ghost" size="sm"
              type="button"
              role="switch"
              aria-checked={tool.enabled}
              aria-disabled={tool.disabled}
              aria-labelledby={`${uid}-${tool.id}-name`}
              aria-describedby={`${uid}-${tool.id}-desc`}
              onClick={() => !tool.disabled && onToggle?.(tool.id, !tool.enabled)}
              className={`relative h-5 w-9 shrink-0 rounded-full transition-colors ${
                tool.enabled ? "bg-primary" : "bg-muted"
              } ${tool.disabled ? "cursor-not-allowed opacity-50" : "cursor-pointer"}`}
            >
              <span
                className={`absolute left-0.5 top-0.5 h-4 w-4 rounded-full bg-white shadow-sm transition-transform ${
                  tool.enabled ? "translate-x-4" : ""
                }`}
              />
            </Button>
          </li>
        ))}
      </ul>
    </Card>
  );
}

When to use it

  • Describe each tool's permission in plain language — 'can read files' beats a tool name.
  • Default tools on when they are safe and clearly benefit the user's goal.
  • Show locked tools as disabled with a reason, not hidden — it motivates the upgrade.
  • Announce what a toggle change means in the next response (web search on → answers cite sources).

What breaks in production

  • Generic tool names with no idea what the tool can actually access.
  • Toggles with no visible consequence — the assistant ignores them or never says it used a tool.
  • Offering an 'allow everything' master switch that nullifies granular controls.

Related Components