Usage
Install
Each icon installs into components/icons/ as source. The shared runtime comes with it, and is reused by every icon after the first.
$ npx shadcn@latest add "https://phosphor-animated.com/r/bell.json"import { Bell } from "@/components/icons/bell";
export function Nav() {
return <Bell className="size-6" />;
}Weights
Five Phosphor weights, from one component. The line weights share a single drawing; duotone adds its filled backdrop.
<Trash weight="thin" />
<Trash weight="bold" />
<Trash weight="duotone" />thin
light
regular
bold
duotone
Triggers
Hover by default. Change it with one prop.
<Heart /> // hover
<Heart trigger="click" />
<Heart trigger="in-view" />
<Heart trigger="loop" />hover
click
in-view
loop
Control it yourself
Set trigger="none" and drive the icon from a ref — for animating on a state change rather than a pointer.
const ref = useRef<AnimatedIconHandle>(null);
<Bell ref={ref} trigger="none" />
<button onClick={() => ref.current?.play()}>
Notify
</button>Speed
A multiplier on playback rate.
<Spinner trigger="loop" speed={2} />speed={0.5}
speed={1}
speed={2}
Reduced motion
Every icon reads prefers-reduced-motion and stays still when the system asks it to. Nothing to configure, and nothing to remember at each call site.
Editing an animation
The keyframes in an installed icon are ordinary data. Change a number and the motion changes — there is nothing to reinstall and nothing pointing back at this site.
const STROKE: Choreography = {
duration: 0.7,
parts: {
0: { rotate: [0, 17, -12, 7, 0], origin: [128, 48] },
1: { rotate: [0, 12, -9, 5, 0], origin: [128, 48] },
},
};