# Toggle Icon Morph — React Toggle Switch

> A switch with a glyph inside the thumb: the two strokes of the cross swing and re-join into a check as the thumb rolls across.

- Category: [Toggles & Switches](https://pixelpapa.com/components/toggles.md)
- Tags: toggle, switch, icon, morph, check
- Dependencies: motion (plus React and Tailwind CSS v4)
- Last updated: 2026-11-13
- Live preview: https://pixelpapa.com/components/toggles/toggle-icon-morph

## Install

```bash
npx shadcn@latest add "https://pixelpapa.com/r/toggle-icon-morph.json"
```

The file is written to `components/pixelpapa/toggle-icon-morph.tsx`. To install manually, run `npm install motion` and copy the source below into your project. It imports `cn` from `@/lib/utils`, which every shadcn project has.

## Source

```tsx
"use client";

import { motion } from "motion/react";
import { useId, useState, type ReactNode } from "react";
import { cn } from "@/lib/utils";

type IconMorphSwitchProps = {
  checked?: boolean;
  defaultChecked?: boolean;
  onCheckedChange?: (checked: boolean) => void;
  /** Visible label; clicking it toggles the switch. */
  children?: ReactNode;
  /** Accessible name when there is no visible label. */
  label?: string;
  tone?: "emerald" | "primary";
  className?: string;
};

const spring = { type: "spring", stiffness: 480, damping: 28 } as const;
const tones = {
  emerald: { track: "bg-emerald-500", glyph: "text-emerald-600" },
  primary: { track: "bg-primary", glyph: "text-primary" },
};

// Same command structure in both states so the paths can tween.
const cross = ["M6 6 L14 14", "M14 6 L6 14"];
const check = ["M5 10.5 L8.6 14", "M15 6.5 L8.6 14"];

export function IconMorphSwitch({
  checked,
  defaultChecked = false,
  onCheckedChange,
  children,
  label,
  tone = "emerald",
  className,
}: IconMorphSwitchProps) {
  const [internal, setInternal] = useState(defaultChecked);
  const on = checked ?? internal;
  const id = useId();

  function toggle() {
    setInternal(!on);
    onCheckedChange?.(!on);
  }

  return (
    <span className={cn("inline-flex items-center gap-3", className)}>
      <button
        id={id}
        type="button"
        role="switch"
        aria-checked={on}
        aria-label={label}
        onClick={toggle}
        className={cn(
          "relative h-8 w-14 shrink-0 rounded-full transition-colors duration-300 [-webkit-tap-highlight-color:transparent]",
          on ? tones[tone].track : "bg-input",
        )}
      >
        <motion.span
          aria-hidden
          initial={false}
          animate={{ x: on ? 24 : 0, rotate: on ? 0 : -90 }}
          whileTap={{ scale: 0.9 }}
          transition={spring}
          className="absolute top-1 left-1 flex size-6 items-center justify-center rounded-full bg-white shadow-[0_1px_3px_rgb(0_0_0/0.25)]"
        >
          <svg
            viewBox="0 0 20 20"
            fill="none"
            className={cn("size-4 transition-colors duration-300", on ? tones[tone].glyph : "text-zinc-400")}
          >
            {[0, 1].map((i) => (
              <motion.path
                key={i}
                initial={false}
                animate={{ d: on ? check[i] : cross[i] }}
                transition={spring}
                stroke="currentColor"
                strokeWidth="2.4"
                strokeLinecap="round"
              />
            ))}
          </svg>
        </motion.span>
      </button>
      {children && (
        <label htmlFor={id} className="cursor-pointer text-sm text-foreground select-none">
          {children}
        </label>
      )}
    </span>
  );
}

export default function ToggleIconMorphDemo() {
  const [published, setPublished] = useState(true);

  return (
    <div className="flex w-full max-w-sm flex-col gap-5">
      <div className="flex items-center justify-between gap-6">
        <div>
          <p className="text-sm font-medium text-foreground">Publish changelog</p>
          <p className="text-xs text-muted-foreground">
            {published ? "Visible at northwind.app/changelog" : "Only your team can see drafts"}
          </p>
        </div>
        <IconMorphSwitch label="Publish changelog" checked={published} onCheckedChange={setPublished} />
      </div>
      <div className="h-px bg-border" />
      <IconMorphSwitch tone="primary" defaultChecked>
        Email me when a teammate comments
      </IconMorphSwitch>
      <IconMorphSwitch tone="primary">Share anonymous usage data</IconMorphSwitch>
    </div>
  );
}
```

## AI prompt

```text
Add the "Toggle Icon Morph" component from PixelPapa UI to this project.

What it is: A switch with a glyph inside the thumb: the two strokes of the cross swing and re-join into a check as the thumb rolls across.
Category: Toggles & Switches. Depends on: motion.

1. Install it with the shadcn CLI:
   npx shadcn@latest add "https://pixelpapa.com/r/toggle-icon-morph.json"
   (If the CLI is unavailable, fetch that URL, write files[0].content to components/pixelpapa/toggle-icon-morph.tsx and install the listed dependencies.)
2. Import it from "@/components/pixelpapa/toggle-icon-morph" and place it where I describe below.
3. Keep its animation timings and accessibility attributes. Replace the demo copy and data with content that fits my product, and use my existing theme tokens (bg-background, text-foreground, bg-primary, border-border) rather than hard-coded colours.

Where to use it: <describe the page and position here>
```

## More toggles & switches

- [Toggle Day Night](https://pixelpapa.com/components/toggles/toggle-day-night.md) (Pro): A theme switch drawn as a tiny sky: the sun rolls across and becomes a cratered moon, clouds sink away and stars pop out one by one.
- [Toggle iOS With Label List](https://pixelpapa.com/components/toggles/toggle-ios-with-label-list.md) (Pro): A settings card of labelled switches under a master toggle; turning the master off folds the dependent rows away with a staggered collapse.
- [Toggle Like Bookmark](https://pixelpapa.com/components/toggles/toggle-like-bookmark.md): Icon toggles that each fill in their own way: the heart pops, the bookmark drops in from above, the star spins and the bell rings, all with a ring burst.
- [Toggle Lock Shake](https://pixelpapa.com/components/toggles/toggle-lock-shake.md) (Pro): A switch that can be locked: clicking it then shakes its head, rattles the padlock in the thumb and pops up the reason it cannot change.
- [Toggle Segmented Theme](https://pixelpapa.com/components/toggles/toggle-segmented-theme.md) (Pro): A light / dark / system picker with a sliding thumb and icons that react when chosen, paired with a miniature window that wipes between themes and splits in two for "system".
- [Toggle Spring Switch](https://pixelpapa.com/components/toggles/toggle-spring-switch.md): A switch whose thumb stretches toward the other side while pressed, then snaps across with a little overshoot on release.
