React
Shape component and morph hooks for React.
The shape-morph/react entry point provides a component and hooks for rendering shapes and morphing in React. Requires React 18+.
<Shape> component
Renders a shape as an inline SVG.
import { Shape } from "shape-morph/react";
<Shape name="Heart" size={32} fill="red" />
<Shape name="Cookie9Sided" size={48} fill="#6750A4" stroke="white" />Props:
| Prop | Type | Default | Description |
|---|---|---|---|
name | ShapeName | required | Shape to render |
size | number | 48 | Width and height in pixels |
fill | string | "currentColor" | Fill color |
stroke | string | - | Stroke color |
strokeWidth | number | - | Stroke width |
className | string | - | Class on the <svg> element |
style | CSSProperties | - | Style on the <svg> element |
useMorph
Animates between two shapes with a requestAnimationFrame loop and ease-in-out interpolation.
import { useMorph } from "shape-morph/react";
function Avatar({ hovered }: { hovered: boolean }) {
const { clipPath, progress } = useMorph("Circle", "Heart", {
progress: hovered ? 1 : 0,
duration: 300,
});
return (
<img
style={{
clipPath,
transform: `rotate(${progress * 90}deg)`,
}}
src="/avatar.png"
/>
);
}Change the target progress and the hook smoothly animates from the current position.
Options
| Option | Type | Default | Description |
|---|---|---|---|
progress | number | 0 | Target progress (0-1) |
duration | number | 300 | Animation duration in ms. Cannot use with lerp or spring. |
easing | (t: number) => number | easeInOut | Easing function for duration-based animation. Cannot use with lerp or spring. |
lerp | number | - | Lerp factor (0-1). Each frame moves this fraction toward target. Cannot use with duration, easing, or spring. |
spring | SpringConfig | - | Spring physics config. Cannot use with duration, easing, or lerp. |
samples | number | 4 | Samples per cubic for polygon output |
size | number | 100 | Output size for pathD |
Return value
| Property | Type | Description |
|---|---|---|
pathD | string | SVG path d attribute |
clipPath | string | CSS clip-path: polygon(...) |
progress | number | Current animated progress. May overshoot 0-1 in spring mode. |
The progress return value is the animated value but not the target. In spring mode it can temporarily overshoot past 0 or 1, which is useful for driving bouncy effects like rotation or scale. The shape output (pathD, clipPath) is always clamped to valid 0-1 range.
Lerp mode
Instead of a fixed duration, lerp mode moves a fraction toward the target each frame. This gives a natural, springy feel without specifying a duration.
const { clipPath } = useMorph("Clover4Leaf", "Heart", {
progress: hovered ? 1 : 0,
lerp: 0.1,
});Spring mode
Spring mode simulates physical spring dynamics. The value overshoots the target, bounces back, and settles naturally. No duration needed. See Spring for details on tuning stiffness and damping.
const { clipPath, progress } = useMorph("Circle", "Heart", {
progress: hovered ? 1 : 0,
spring: { stiffness: 180, damping: 12 },
});
<div style={{
clipPath,
// progress can overshoot 0-1, giving a bouncy rotation
transform: `rotate(${progress * 90}deg)`,
}} />Custom easing
Override the default easing function with any (t: number) => number function. See Easing for built-in presets.
import { easeInOutCubic } from "shape-morph";
const { clipPath } = useMorph("Circle", "Heart", {
progress: hovered ? 1 : 0,
duration: 500,
easing: easeInOutCubic,
});useShape
Returns path data for a single shape. No animation.
import { useShape } from "shape-morph/react";
const { pathD, clipPath } = useShape("Heart", 100);Return value
| Property | Type | Description |
|---|---|---|
pathD | string | SVG path d attribute |
clipPath | string | CSS clip-path: polygon(...) |
polygon | RoundedPolygon | The underlying polygon instance |