A spring that solves your animation problems.
—
The Motion component provides single element animations using spring physics. It's the simplest and most commonly used component in React Motion, ideal for basic state transitions, hover effects, and straightforward UI animations.
Creates a single animated element that transitions between style states using spring physics.
/**
* Single element animation component using spring physics
* Animates from defaultStyle (or current style) to target style
*/
class Motion extends React.Component {
static propTypes = {
/** Initial style values (optional, defaults to target style values) */
defaultStyle: PropTypes.objectOf(PropTypes.number),
/** Target style with spring configurations (required) */
style: PropTypes.objectOf(PropTypes.oneOfType([
PropTypes.number,
PropTypes.object,
])).isRequired,
/** Render function receiving interpolated style values (required) */
children: PropTypes.func.isRequired,
/** Callback fired when animation completes (optional) */
onRest: PropTypes.func,
};
}Usage Examples:
import React, { useState } from 'react';
import { Motion, spring } from 'react-motion';
// Basic animation
function BasicMotion() {
const [open, setOpen] = useState(false);
return (
<Motion
defaultStyle={{width: 0, opacity: 0}}
style={{
width: spring(open ? 200 : 0),
opacity: spring(open ? 1 : 0)
}}
>
{({width, opacity}) => (
<div
style={{
width: `${width}px`,
opacity,
background: 'blue',
overflow: 'hidden'
}}
>
<button onClick={() => setOpen(!open)}>
Toggle
</button>
</div>
)}
</Motion>
);
}
// With custom spring config
function CustomSpringMotion() {
const [x, setX] = useState(0);
return (
<Motion
style={{
x: spring(x, {stiffness: 120, damping: 17})
}}
>
{({x}) => (
<div
style={{transform: `translateX(${x}px)`}}
onClick={() => setX(x === 0 ? 100 : 0)}
>
Click to move
</div>
)}
</Motion>
);
}
// With onRest callback
function MotionWithCallback() {
const [scale, setScale] = useState(1);
const [animating, setAnimating] = useState(false);
return (
<Motion
style={{scale: spring(scale)}}
onRest={() => {
setAnimating(false);
console.log('Animation completed!');
}}
>
{({scale}) => (
<div
style={{
transform: `scale(${scale})`,
transition: animating ? 'none' : undefined
}}
onClick={() => {
setAnimating(true);
setScale(scale === 1 ? 1.5 : 1);
}}
>
{animating ? 'Animating...' : 'Click to scale'}
</div>
)}
</Motion>
);
}Optional initial style values. If not provided, the component will extract initial values from the target style.
/**
* Initial style values for the animation
* If omitted, values are extracted from target style
*/
defaultStyle?: PlainStyle;Target style object containing numeric values or spring configurations. This is where you define what the animation should transition to.
/**
* Target style with spring configurations
* Can mix plain numbers with spring() calls
*/
style: Style;Render function that receives the current interpolated style values and returns a React element.
/**
* Render function receiving interpolated style values
* Called on every animation frame with current values
*/
children: (interpolatedStyle: PlainStyle) => ReactElement;Optional callback function fired when the animation reaches its target and stops moving.
/**
* Callback fired when animation completes
* Useful for chaining animations or triggering side effects
*/
onRest?: () => void;The Motion component uses a spring-damper physics system:
requestAnimationFrame for smooth 60fps animationsInternally tracks:
currentStyle: Current interpolated valuescurrentVelocity: Current velocity for each animated propertylastIdealStyle: Target values from previous framelastIdealVelocity: Target velocity from previous frameThis state enables smooth interruptions when style prop changes during animation.
<Motion
style={{
x: spring(condition ? 100 : 0),
opacity: spring(visible ? 1 : 0)
}}
>
{({x, opacity}) => (
<div style={{transform: `translateX(${x}px)`, opacity}}>
Content
</div>
)}
</Motion><Motion
style={{
width: spring(expanded ? 200 : 50),
height: spring(expanded ? 200 : 50),
rotate: spring(expanded ? 45 : 0),
borderRadius: spring(expanded ? 20 : 5)
}}
>
{({width, height, rotate, borderRadius}) => (
<div
style={{
width: `${width}px`,
height: `${height}px`,
transform: `rotate(${rotate}deg)`,
borderRadius: `${borderRadius}px`
}}
>
Morphing box
</div>
)}
</Motion>function ChainedAnimation() {
const [stage, setStage] = useState(0);
return (
<Motion
style={{
x: spring(stage === 0 ? 0 : stage === 1 ? 100 : 200),
y: spring(stage < 2 ? 0 : 100)
}}
onRest={() => {
if (stage < 2) {
setStage(stage + 1);
}
}}
>
{({x, y}) => (
<div style={{transform: `translate(${x}px, ${y}px)`}}>
Stage {stage}
</div>
)}
</Motion>
);
}Install with Tessl CLI
npx tessl i tessl/npm-react-motion