animated-icons — Motion-animated icons via the shadcn registries
Adds one pre-built, Motion-animated icon to a Next.js app instead of hand-rolling an SVG animation. Two registries ship them as shadcn components, both MIT, both on motion, both copied into your repo as source you then own:
| Registry |
Icons |
Namespace |
Extra file |
| heroicons-animated |
316 |
@heroicons-animated/<name> |
— each icon is self-contained |
| hugeicons-animated |
165 |
@hugeicons-animated/<name> |
lib/use-icon-animation.ts, a shared hook installed once |
Pick the one that matches the project's static set, not the one with more icons: an animated Heroicon next to Hugeicons outlines reads as a different drawing, because it is. Read meta.json#stack.icon_library (or the DESIGN.md icon block) before choosing, and say which you picked and why.
This is the ecosystem-first move for animated icons — the same rule as everywhere in dev-flow: don't hand-animate a bell/heart/menu SVG when a maintained, tokenizable component exists. This skill owns the install plus the two things the raw components don't give you: an accessibility guard (they animate unconditionally) and timing aligned to your motion tokens.
When this skill applies
- "animated icon", "animate this icon", "make the bell/heart/menu icon animate on hover", "aggiungi un'icona animata", an icon micro-interaction (notification bell shake, menu↔close morph, heart like).
If the user wants a whole motion system (durations/easings/tiers), that's transitions. If they want a static icon, that's the plain heroicons/lucide set from the UI library. If the motion runtime isn't wired yet, this skill routes to module-add motion first (the components import from motion/react).
Contract
Follows the dev-flow contract — see references/contracts.md. Key facts:
- Reads
meta.json#stack.framework — refuses if not {"next", "monorepo"} (web). React Native → rn-animations-gestures.
- Requires the
motion runtime (each icon imports from motion/react). If absent, route to module-add motion first.
- Requires a shadcn-configured project (
components.json). If the project uses Base UI/MUI without shadcn CLI, the components still work (they're plain motion + SVG) but you install them by hand — see references/usage.md.
- Records nothing structural and does not bump
phase — appends a history entry per install. Horizontal capability.
Install one icon
- Ensure the
motion runtime — package.json has motion (or run module-add motion; the components import { motion, useAnimation } from "motion/react").
- Register the namespaced registry once in
components.json (shadcn CLI v4 namespaced registries, the same mechanism as coss-ui's @coss/*):// components.json
"registries": {
"@heroicons-animated": "https://www.heroicons-animated.com/r/{name}.json",
"@hugeicons-animated": "https://hugeicons-animated.com/r/{name}.json"
}
- Add the icon —
pnpm dlx shadcn@latest add @heroicons-animated/<name> (e.g. bell, heart, bars-3) or @hugeicons-animated/<name> (e.g. add-circle, alarm-clock, arrow-right-02). It writes one .tsx to your components dir and installs motion (the item's only dependency).
Flags confirmed at shadcn@4.19.0 from the CLI's own add definition: -o, --overwrite
("overwrite existing files"), plus three worth knowing for a copy-in registry —
--view [path] ("show file contents"), --diff [path] ("show diff for a file") and
--dry-run ("preview changes without writing files"). Since the component becomes your file
the moment it lands, --view before the first add and --diff before an --overwrite are the two
that save you. Also available: -y/--yes, -a/--all, -p/--path, -s/--silent, -c/--cwd.
Use it (real component API — read off the registry source, 2026-08-26)
Each icon exports a <Name>Icon component and a <Name>IconHandle ref type. Props extend HTMLAttributes<HTMLDivElement> plus size (default 28).
"use client"; // motion forces a client component
import { BellIcon, type BellIconHandle } from "@/components/ui/bell";
import { useRef } from "react";
// (a) hover — animates automatically on pointer-enter, no wiring
<BellIcon size={20} className="text-muted-foreground" />
// (b) controlled — trigger from your own event (e.g. a new notification)
const bell = useRef<BellIconHandle>(null);
bell.current?.startAnimation(); // and .stopAnimation()
<BellIcon ref={bell} />
The two value-adds this skill enforces
prefers-reduced-motion guard (a11y). ⚠️ Check the registry before assuming, because the two differ — verified by installing from both at shadcn@4.19.0:
- hugeicons-animated — the shared
lib/use-icon-animation.ts it installs alongside the first icon already calls useReducedMotion() and makes startAnimation() a no-op when it is set. The hook is the guard.
- heroicons-animated — each icon is self-contained and animates on hover unconditionally; the guard is yours to add.
Either way, the trigger you write needs its own check: hover mode is the component's business, but bell.current?.startAnimation() fired from your own event is your call, and calling it for a user who opted out is your bug, not the hook's. So keep the guard at the call site regardless of registry:
import { useReducedMotion } from "motion/react";
const reduce = useReducedMotion();
// hover mode: pass a prop / conditionally render the static heroicon when `reduce`
// controlled mode: `if (!reduce) bell.current?.startAnimation()`
Where an animated icon is purely decorative and reduce is set, prefer the static heroicon of the same name.
Token the timing. The components hardcode their own durations/easings. When you control an icon's transition (wrappers, follow-on effects), align it to the project's lib/motion/tokens.ts (see transitions) so icon motion matches the system feel instead of drifting.
Companion skills — what this owns vs reuses
module-add motion — installs the motion runtime these icons depend on. This skill routes there; it never re-installs the runtime.
transitions — owns the motion discipline (tiers, tokens, reduced-motion). this skill is a Tier-3 (Motion) icon source under that discipline; transitions is where the reduced-motion + tokenization rules live.
- shadcn (
design-md-to-app / the UI library) — owns components.json and the static icon set. Animated icons complement the static heroicons; mix freely (static in dense UI, animated for a few meaningful affordances).
rn-animations-gestures — the React Native counterpart (this registry is web/DOM-only).
Definition of Done
- The requested icon's
.tsx exists in the components dir; motion is in package.json.
- Usage includes the reduced-motion guard (no unconditional animation).
- Decorative-only icons fall back to the static heroicon under
prefers-reduced-motion.
history appended; no phase bump.
What this skill does NOT do
- Doesn't install the runtime — that's
module-add motion.
- Doesn't define a motion system — that's
transitions.
- Doesn't do React Native — use
rn-animations-gestures.
- Doesn't replace static icons — it adds a few animated affordances; most icons stay static.
Reference files
references/usage.md — the real per-icon component API (hover + imperative handle), the reduced-motion patterns, hand-install (non-shadcn) path, and the icon-name list pointer.
references/contracts.md — the .workflow/ dev-flow contract (vendored).
1---2name: animated-icons3description: Add a Motion-animated icon to a Next.js app from a shadcn registry instead of hand-animating an SVG. Two registries, picked from the project's icon set: **heroicons-animated** (316 icons, `shadcn add @heroicons-animated/<name>`) and **hugeicons-animated** (165 icons, `shadcn add @hugeicons-animated/<name>`, which also installs a shared `lib/use-icon-animation.ts`). Both MIT, both on the `motion` runtime, both copied into your repo as source. This skill owns the install plus the two things the raw components lack: a `prefers-reduced-motion` guard and timing aligned to the project motion tokens (`lib/motion/`). Use when the user says "animated icon", "animate this icon", "make the bell/heart/menu icon animate on hover", "aggiungi un'icona animata", or wants an icon micro-interaction. Refuses outside Next.js web — for React Native use `rn-animations-gestures`. Not for: static icons, a whole motion system (`transitions`), or installing the runtime itself (`module-add motion`).4---56# animated-icons — Motion-animated icons via the shadcn registries78Adds one **pre-built, Motion-animated icon** to a Next.js app instead of hand-rolling an SVG animation. Two registries ship them as shadcn components, both MIT, both on `motion`, both copied into your repo as source you then own:910| Registry | Icons | Namespace | Extra file |11|---|---:|---|---|12| [heroicons-animated](https://www.heroicons-animated.com/) | 316 | `@heroicons-animated/<name>` | — each icon is self-contained |13| [hugeicons-animated](https://hugeicons-animated.com/) | 165 | `@hugeicons-animated/<name>` | **`lib/use-icon-animation.ts`**, a shared hook installed once |1415**Pick the one that matches the project's static set**, not the one with more icons: an animated Heroicon next to Hugeicons outlines reads as a different drawing, because it is. Read `meta.json#stack.icon_library` (or the DESIGN.md icon block) before choosing, and say which you picked and why.1617This is the **ecosystem-first** move for animated icons — the same rule as everywhere in dev-flow: don't hand-animate a bell/heart/menu SVG when a maintained, tokenizable component exists. This skill owns the **install** plus the two things the raw components *don't* give you: an **accessibility guard** (they animate unconditionally) and **timing aligned to your motion tokens**.1819## When this skill applies2021- "animated icon", "animate this icon", "make the bell/heart/menu icon animate on hover", "aggiungi un'icona animata", an icon micro-interaction (notification bell shake, menu↔close morph, heart like).2223If the user wants a **whole motion system** (durations/easings/tiers), that's `transitions`. If they want a **static** icon, that's the plain heroicons/lucide set from the UI library. If the `motion` runtime isn't wired yet, this skill routes to `module-add motion` first (the components import from `motion/react`).2425## Contract2627Follows the dev-flow contract — see `references/contracts.md`. Key facts:2829- Reads `meta.json#stack.framework` — **refuses** if not `{"next", "monorepo"}` (web). React Native → `rn-animations-gestures`.30- Requires the **`motion`** runtime (each icon `import`s from `motion/react`). If absent, route to `module-add motion` first.31- Requires a **shadcn-configured** project (`components.json`). If the project uses Base UI/MUI without shadcn CLI, the components still work (they're plain `motion` + SVG) but you install them by hand — see `references/usage.md`.32- Records nothing structural and **does not bump `phase`** — appends a `history` entry per install. Horizontal capability.3334## Install one icon35361. **Ensure the `motion` runtime** — `package.json` has `motion` (or run `module-add motion`; the components `import { motion, useAnimation } from "motion/react"`).372. **Register the namespaced registry once** in `components.json` (shadcn CLI v4 namespaced registries, the same mechanism as `coss-ui`'s `@coss/*`):38 ```jsonc39 // components.json40 "registries": {41 "@heroicons-animated": "https://www.heroicons-animated.com/r/{name}.json",42 "@hugeicons-animated": "https://hugeicons-animated.com/r/{name}.json"43 }44 ```453. **Add the icon** — `pnpm dlx shadcn@latest add @heroicons-animated/<name>` (e.g. `bell`, `heart`, `bars-3`) or `@hugeicons-animated/<name>` (e.g. `add-circle`, `alarm-clock`, `arrow-right-02`). It writes one `.tsx` to your components dir and installs `motion` (the item's only dependency).4647**Flags confirmed at `shadcn@4.19.0`** from the CLI's own `add` definition: `-o, --overwrite`48(*"overwrite existing files"*), plus three worth knowing for a copy-in registry —49**`--view [path]`** (*"show file contents"*), **`--diff [path]`** (*"show diff for a file"*) and50**`--dry-run`** (*"preview changes without writing files"*). Since the component becomes *your* file51the moment it lands, `--view` before the first add and `--diff` before an `--overwrite` are the two52that save you. Also available: `-y/--yes`, `-a/--all`, `-p/--path`, `-s/--silent`, `-c/--cwd`.5354## Use it (real component API — read off the registry source, 2026-08-26)5556Each icon exports a `<Name>Icon` component **and** a `<Name>IconHandle` ref type. Props extend `HTMLAttributes<HTMLDivElement>` plus `size` (default `28`).5758```tsx59"use client"; // motion forces a client component60import { BellIcon, type BellIconHandle } from "@/components/ui/bell";61import { useRef } from "react";6263// (a) hover — animates automatically on pointer-enter, no wiring64<BellIcon size={20} className="text-muted-foreground" />6566// (b) controlled — trigger from your own event (e.g. a new notification)67const bell = useRef<BellIconHandle>(null);68bell.current?.startAnimation(); // and .stopAnimation()69<BellIcon ref={bell} />70```7172## The two value-adds this skill enforces73741. **`prefers-reduced-motion` guard (a11y).** ⚠️ **Check the registry before assuming, because the two differ** — verified by installing from both at `shadcn@4.19.0`:75 * **hugeicons-animated** — the shared `lib/use-icon-animation.ts` it installs alongside the first icon **already calls `useReducedMotion()`** and makes `startAnimation()` a no-op when it is set. The hook is the guard.76 * **heroicons-animated** — each icon is self-contained and animates on hover **unconditionally**; the guard is yours to add.7778 Either way, **the trigger you write needs its own check**: hover mode is the component's business, but `bell.current?.startAnimation()` fired from your own event is your call, and calling it for a user who opted out is your bug, not the hook's. So keep the guard at the call site regardless of registry:79 ```tsx80 import { useReducedMotion } from "motion/react";81 const reduce = useReducedMotion();82 // hover mode: pass a prop / conditionally render the static heroicon when `reduce`83 // controlled mode: `if (!reduce) bell.current?.startAnimation()`84 ```85 Where an animated icon is purely decorative and `reduce` is set, prefer the **static** heroicon of the same name.862. **Token the timing.** The components hardcode their own durations/easings. When you control an icon's transition (wrappers, follow-on effects), align it to the project's `lib/motion/tokens.ts` (see `transitions`) so icon motion matches the system feel instead of drifting.8788## Companion skills — what this owns vs reuses8990- **`module-add motion`** — installs the `motion` runtime these icons depend on. This skill routes there; it never re-installs the runtime.91- **`transitions`** — owns the motion **discipline** (tiers, tokens, reduced-motion). this skill is a **Tier-3 (Motion) icon source** under that discipline; `transitions` is where the reduced-motion + tokenization rules live.92- **shadcn** (`design-md-to-app` / the UI library) — owns `components.json` and the static icon set. Animated icons **complement** the static heroicons; mix freely (static in dense UI, animated for a few meaningful affordances).93- **`rn-animations-gestures`** — the React Native counterpart (this registry is web/DOM-only).9495## Definition of Done9697- The requested icon's `.tsx` exists in the components dir; `motion` is in `package.json`.98- Usage includes the **reduced-motion guard** (no unconditional animation).99- Decorative-only icons fall back to the static heroicon under `prefers-reduced-motion`.100- `history` appended; **no `phase` bump**.101102## What this skill does NOT do103104- **Doesn't install the runtime** — that's `module-add motion`.105- **Doesn't define a motion system** — that's `transitions`.106- **Doesn't do React Native** — use `rn-animations-gestures`.107- **Doesn't replace static icons** — it adds a few animated affordances; most icons stay static.108109## Reference files110111- `references/usage.md` — the real per-icon component API (hover + imperative handle), the reduced-motion patterns, hand-install (non-shadcn) path, and the icon-name list pointer.112- `references/contracts.md` — the `.workflow/` dev-flow contract (vendored).