Icon morph

Icons that morph between states: menu to close, play to pause, copy to check, and more.

pnpm dlx shadcn@latest add @uiarc/icon-morph
Live · keyboard ready

  • Icon buttons that toggle between two states, such as a hamburger menu or a play control.
  • Copy and add actions that should confirm with a check in place.
  • Like and notify toggles that deserve a small moment of delight.
  • Use copy-button for a complete copy-to-clipboard control with its own feedback.
  • Use theme-switch for a labelled light and dark toggle wired to the page theme.
  • Use a plain lucide icon when the icon never changes state.

Installation

Add Icon morph with the shadcn CLI, or copy the source by hand.

pnpm dlx shadcn@latest add @uiarc/icon-morph

Adds the component and its local dependencies, and installs motion. First time? Add the @uiarc registry to components.json, or use the full URL:

example.tsx
import { useState } from "react";import { IconMorph } from "@/components/arc/icon-morph/icon-morph"; export function MenuToggle() {  const [open, setOpen] = useState(false);  return (    <button type="button" aria-label="Menu" aria-expanded={open} onClick={() => setOpen(value => !value)}>      <IconMorph name="menu" state={open} size={20} />    </button>  );}

API reference

9 parts. The first is the root.

IconMorph

One entry point for eight two-state icons that morph instead of swapping. Pick one by name and drive it with state.

PropTypeDefaultDescription
nameRequired"menu" | "play" | "plus" | "copy" | "theme" | "chevron" | "bell" | "heart"–Which icon to render.
statebooleanfalseThe second state: menu to close, play to pause, plus or copy to check, sun to moon, chevron turned, bell on, heart liked.
sizenumber24Rendered size in px.
strokeWidthnumber1.75Stroke width in the 24 unit grid.
...propsOmit<SVGProps<SVGSVGElement>, "ref" | "children">–Forwarded to the svg.

MenuIcon

Menu to close: outer lines rotate into an X and the middle line folds away. Takes the same props as IconMorph without name.

No props.

PlayPauseIcon

Play to pause: the triangle splits into two halves that straighten into bars.

No props.

PlusCheckIcon

Plus to check: each bar swings its ends into one leg of the check.

No props.

CopyCheckIcon

Copy to check: the sheets tuck away and a check draws in.

No props.

SunMoonIcon

Sun to moon: the disc grows while a shadow carves the crescent and the rays fold in.

No props.

ChevronIcon

A chevron that turns on a spring, for disclosure and sort controls.

PropTypeDefaultDescription
turnnumber180Degrees the chevron turns when state is true. 180 flips down to up; 90 turns right to down.
pointing"down" | "right""down"Where the chevron points at rest.

BellIcon

Notifications: turning on rings the bell with a lagging clapper; turning off strikes it through.

No props.

HeartIcon

Like: the heart fills with a pop and a ring and sparks burst once. Liked color is --icon-morph-like, default --danger.

No props.

  • Every icon renders aria-hidden and focusable="false"; put it inside a button that carries the accessible name.
  • Reflect the state on the button with aria-pressed, aria-expanded, or a changing label, since the icon itself says nothing.
  • Lines rotate and slide, and shapes interpolate their path data, all on interruptible springs, so fast toggling reverses from wherever the icon is.
  • The bell ring and heart burst are one-shot moments that replay on each turn on.
  • Reduced motion makes every morph instant and skips the ring and burst.
  • Icons are fixed-size SVGs on a 24 unit grid; set size per breakpoint from the parent.
  • Pair with a hit area of at least 40px on touch; the icon itself adds none.
  • Each icon animates a few SVG attributes with motion; many on one screen are cheap.
  • SunMoonIcon and BellIcon use an SVG mask with a per-instance id from useId.

Notes for AI

Give your coding assistant the Markdown reference instead of screenshots.

  • Use inside icon buttons whose meaning flips between two states: menu and close, play and pause, copy and copied, like and liked.
  • Import the specific icon (HeartIcon, ChevronIcon) when only one is needed; IconMorph is a name switch over the same components.
  • Icons use currentColor, so color them from the parent button.
  • For a whole button that changes label and state use action-swap; for theme switching with a label use theme-switch.

The full library index for assistants is at /llms.txt.