React-Move Animation Library
React-Move is a highly performant, data-driven animation library for React applications, supporting animations for HTML, SVG, and React-Native elements. Currently stable at version 6.5.0, it targets React 16.3 and above, focusing on a minimal bundle size (3.5kb gzipped). The library provides fine-grained control over animation properties like delay, duration, and easing, along with lifecycle events for 'start', 'interrupt', and 'end'. A key differentiator is its flexible approach to interpolation; while it handles numeric interpolation by default, it allows integration with libraries like `d3-interpolate` for complex types such as colors, paths, and SVG transforms. Major versions often align with React's lifecycle method changes, with a commitment to API stability between major releases. It ships with full TypeScript support, making it well-suited for modern web development. While v5.x provides backward compatibility for older React versions, v6.x is recommended for React 16.3+ to leverage a smaller bundle size by omitting the `react-lifecycles-compat` polyfill.
Common errors
-
TypeError: Cannot read properties of undefined (reading 'interpolate')
cause Attempting to use `interpolate` or `interpolateTransformSvg` from `d3-interpolate` in the `interpolation` prop without `d3-interpolate` being installed.fixInstall `d3-interpolate` as a dependency: `npm install d3-interpolate`. Ensure it is imported correctly where used: `import { interpolate } from 'd3-interpolate'`. -
Error: Invalid hook call. Hooks can only be called inside of the body of a function component.
cause This error often indicates a React version mismatch, specifically using `react-move` v6 (which uses React 16.3+ lifecycle methods) with an older React version (e.g., <16.3.0).fixUpgrade your React peer dependency to `^16.3.0` or higher. Alternatively, if upgrading React is not an option, downgrade `react-move` to version 5.x (`npm install react-move@^5.0.0`).
Warnings
- breaking React-Move v6.0.0 (and v3.0.0) requires React 16.3.0 or greater due to the adoption of new lifecycle methods. Using it with older React versions will result in runtime errors related to hooks or component lifecycles.
- breaking Since v4.0.0, `d3-interpolate` is no longer a hard dependency. If you require advanced interpolation for non-numeric values (e.g., colors, SVG paths, SVG transforms), you must explicitly install `d3-interpolate` and pass a custom `interpolation` prop to `NodeGroup` or `Animate`.
- gotcha Version 5.x of React-Move includes `react-lifecycles-compat` for compatibility with older React versions, which slightly increases the bundle size. For React versions 16.3.0 and newer, using `react-move@^6.0.0` is recommended to get the smallest possible bundle size.
Install
-
npm install react-move -
yarn add react-move -
pnpm add react-move
Imports
- NodeGroup
const { NodeGroup } = require('react-move')import { NodeGroup } from 'react-move' - Animate
const Animate = require('react-move').Animateimport { Animate } from 'react-move' - NodeGroupProps
import { NodeGroupProps } from 'react-move'import type { NodeGroupProps } from 'react-move'
Quickstart
import React, { useState } from 'react';
import { NodeGroup } from 'react-move';
import { interpolate, interpolateTransformSvg } from 'd3-interpolate';
interface DataItem {
name: string;
x: number;
y: number;
transform: string;
}
const AnimatedNodes: React.FC = () => {
const [data, setData] = useState<DataItem[]>([
{ name: 'a', x: 0, y: 0, transform: 'translate(0,0) scale(1)' },
{ name: 'b', x: 50, y: 50, transform: 'translate(50,50) scale(1)' },
]);
const updateData = () => {
setData(prevData => prevData.map(d => ({
...d,
x: Math.random() * 180 + 10,
y: Math.random() * 180 + 10,
transform: `translate(${Math.random() * 100},${Math.random() * 100}) scale(${0.5 + Math.random() * 1.5})`
})));
};
return (
<div>
<button onClick={updateData} style={{ marginBottom: '10px' }}>Animate Nodes</button>
<svg width="200" height="200" style={{ border: '1px solid #eee' }}>
<NodeGroup
data={data}
keyAccessor={(d: DataItem) => d.name}
start={(data: DataItem) => ({
x: data.x,
y: data.y,
transform: data.transform,
opacity: 0,
})}
enter={(data: DataItem) => ([
{
opacity: [1],
timing: { duration: 500 },
},
{
x: [data.x],
y: [data.y],
transform: [data.transform],
timing: { duration: 500 },
}
])}
update={(data: DataItem) => ({
x: [data.x],
y: [data.y],
transform: [data.transform],
opacity: [1],
timing: { duration: 500, ease: 'easeCubicOut' },
})}
leave={() => ([
{
opacity: [0],
timing: { duration: 300 },
},
{
x: [0],
y: [0],
timing: { duration: 300 },
}
])}
interpolation={(begValue, endValue, attr) => {
if (attr === 'transform') {
return interpolateTransformSvg(begValue, endValue);
}
return interpolate(begValue, endValue);
}}
>
{nodes => (
<g>
{nodes.map(({ key, data: item, state }) => (
<circle
key={key}
cx={state.x}
cy={state.y}
r={10}
fill="steelblue"
opacity={state.opacity}
transform={state.transform}
/>
))}
</g>
)}
</NodeGroup>
</svg>
</div>
);
};
export default AnimatedNodes;