Introducing v9.0
This page explains every feature introduced in v9.
For breaking changes, go here.
🎨 The examples with graphics are editable.
Platform packages
While you can still import react-spring for the web version, you can now use
@react-spring/web if you'd prefer to avoid installing the wrapper package for
every other supported platform. You can use Yarn aliases to make it seamless.
yarn add react-spring@npm:@react-spring/web@9.0.0-rc.1
Every supported platform has its own package, which depends on @react-spring/core.
The react-spring package re-exports the package of every supported platform,
which means you can import react-spring/native like you could in v8.
The react-spring/universal module has been replaced by @react-spring/core.
Platform interop
In v9, you can use multiple "render targets" on the same page. This means you
can animate 3d objects with @react-spring/three while animating DOM elements
with @react-spring/web on the same page! 🤩
Click here for a demo.
UMD bundle
Currently, only the @react-spring/web package has a UMD bundle.
Click here for the latest version.
https://cdn.jsdelivr.net/npm/@react-spring/web@next/index.umd.min.js
Unified API
In the past two major versions, the "Render Props API" and "Hooks API" were
provided by separate modules. In fact, each API had its own implementation of
the internal Controller class, which was bad for maintenance.
// v8import { useSpring } from 'react-spring'import { Spring } from 'react-spring/renderprops'
The new version unifies these APIs into a single module!
// v9import { Spring, useSpring } from 'react-spring'
Revamped types
TypeScript support has undergone a facelift.
- Most types have been renamed, modified, or removed.
- Not yet 100% stable. Please report any rough edges! ✌️
Any questions can be directed here.
🚧 More type documentation coming soon.
The useTransition rework
Learn about the new useTransition on the Breaking Changes page.
Concurrent mode
Support for React's concurrent mode has been added.
Please report any issues you may find. 😇
Dependency arrays
Every hook except useChain now supports a deps argument, which is an
array exactly like what useEffect takes.
Whenever a dependency is changed, the update will be processed and thus the animation will be updated. But otherwise, updates are ignored when all dependencies are unchanged.
When a dependency array is passed, the useSpring and useSprings hooks
each return an animate function.
const [style, animate] = useSpring({ x: 0, y: 0 }, [])useEffect(() => {animate({ x: 100, y: 100 })}, [])
Shorthand style props
In the web version only, you can avoid the boilerplate of using the to
function to combine multiple animated values into a transform string like you
had to do in v8.
Instead, you can try defining an animated value in the style object using one
of the following shorthand props:
x,y,ztranslate,translateX,translateY,translate3drotate,rotateX,rotateY,rotate3dscale,scaleX,scaleY,scale3dskew,skewX,skewYmatrix,matrix3d
const { x, y } = useSpring({ x: 0, y: 0 })// v8import { to } from 'react-spring'const transform = to([x, y], (x, y) => `translate(${x}px, ${y}px)`)return <a.div style={{ transform }} />// v9return <a.div style={{ x, y }} />
These shorthand props will be added to @react-spring/native in the future.
For now, they only exist in @react-spring/web.
The loop prop
Use loop: true to repeat an animation.
The loop function
Pass a function to be called after each loop. Return true to continue
looping, or false to stop.
The loop function can also return a loop object, which is described in the
next section. This is useful when you want to decide the next loop animation
right after the previous loop is finished.
The loop object
Define a loop object to customize the loop animation separately from the
initial animation. It may contain any of the useSpring props. For example,
if reverse: true is used, the loop animation will reverse on each loop.
Inherited props
The loop object is always merged into a copy of the props object it was
defined in. The following example shows a loop animation that inherits its
config prop.
⚠️ Notice how the loop doesn't run more than once. That's because some props
are never inherited. These props include default, reset, and reverse.
To loop the animation, try adding reset: true to the loop prop in the above
example. Alternatively, you could add from: { x: 0 } to get the same effect.
Lastly, try adding config: { friction: 5 } to the loop object. This overrides
the inherited config.duration with a springy animation.
Dynamic goals
The FrameValue class (inherited by SpringValue and Interpolation) allows for
animations toward or from an observable FluidValue object. You can make any library
work like this with react-spring by following this guide.
Try using immediate: true to make rotateZ stick to its FluidValue instead
of animating toward it.
The from prop can also be a FluidValue, but it only caches the FluidValue's
current value when first animated and when the reset prop is true. In other
words, nothing will happen if the from value is changed during an animation.
The cancel prop
When true, the cancel prop stops the animation of every animated value
owned by the Controller that receives it.
The following example never animates, because cancel is always true.
useSpring({cancel: true,from: { x: 0 },to: { x: 100 },})
Once the cancel prop turns from true to false, the declared animation
will resume or reset, depending on the given props.
Delayed updates
The cancel prop even prevents delayed updates. 😎
const [style, animate] = useSpring(() => ({ x: 0 }))// Pass this to an element.const onClick = () => {animate({ x: 100, delay: 500 })// Prevent the previous update. 🤣animate({ cancel: true })}
Specific keys
To cancel the animation of specific keys, you can pass a single key, an array of
keys, or a (key: string) => boolean function.
// Using a single keycancel: 'x',// Using an array of keyscancel: ['x', 'y'],// Using a functioncancel: key => key !== 'x',
The reset and immediate props support these same types.
The pause prop
The pause prop literally freezes animations in time.
When used in a declarative update, the declared animation is paused until the
pause prop equals false again. Then the animation will continue from its
current value.
The following example will pause the animation while your mouse is over it.
(Note: The useHover hook is theoretical)
const isHovering = useHover()useSpring({pause: isHovering,from: { x: 0 },to: { x: 100 },loop: true,})
Animation results
The onRest prop receives an AnimationResult object when called. It contains
the following properties:
value: anyThe current value when the animation ended.target: objectThe object being animated (either aSpringValueorController).finished?: booleanEquals true when the animation finished before being stopped or cancelled.cancelled?: booleanEquals true when the animation was cancelled.
The promise returned by the ref API's start method is resolved with an
AnimationResult object, as well as the start method of the SpringValue
and Controller classes.
Config props
The config prop existed in v8.x, but v9.x brings a few improvements.
Partial updates
The config prop can now be partially updated.
const { x } = useSpring({from: { x: 0 },config: { frequency: 1 },})useEffect(() => {x.start({ config: { velocity: 0 } })x.start({ config: { friction: 20 } })}, [])
Additions
The following props have been added:
frequency?: numberThe natural frequency (in seconds), which dictates the number of bounces per second when no damping exists.
When defined,
tensionis derived from this, andfrictionis derived fromtensionanddamping.damping?: numberThe damping ratio, which dictates how the spring slows down.
Set to
0to never slow down. Set to1to slow down without bouncing. Between0and1is for you to explore.Only works when
frequencyis defined. Defaults to1.round?: numberWhile animating, round to the nearest multiple of this number. The
fromandtovalues are never rounded, as well as any value passed to thesetmethod of an animated value.bounce?: numberWhen above zero, the spring will bounce instead of overshooting when exceeding its goal value.
Its velocity is multiplied by
-1 + bouncewhenever its current value equals or exceeds its goal. For example, settingbounceto0.5chops the velocity in half on each bounce, in addition to any friction.progress?: numberWhen used with
duration, it decides how far into the easing function to start from. The duration itself is unaffected.Defaults to
0, which means "start from the beginning".Setting to
1+makes an immediate animation. Setting to0.5means "start from the middle of the easing function". Any number>= 0and<= 1makes sense here.restVelocity?: numberThe smallest velocity before the animation is considered to be "not moving". When undefined,
precisionis used instead.
Default props
The default prop lets you set the default value of certain props defined in
the same update.
Declarative updates
For the declarative API, this prop is true by default. In the following
example, the onRest handler is inherited by every update passed to animate,
except when a call has its own onRest prop defined.
useSpring({to: async animate => { ... },onRest: () => { ... }})
Imperative updates
Imperative updates can use default: true to set default props. When an
async to prop is defined by an imperative update, it will inherit props from
the imperative update like this:
useSpring({from: { x: 0 },to: async animate => {// The `config` prop below is inherited by// both objects in the `to` array.await animate({to: [{ x: 100 }, { x: 0 }],config: { tension: 100 },})},})
Default props are inherited while an update is being processed by the internal
diffing algorithm within the SpringValue#_merge method.
For example, both the ref prop and the animate function will inherit
the default props defined below (in the useSpring props function).
const ref = useRef()const [{ x }, animate] = useSpring(() => ({x: 0,onRest: () => { ... },ref,}))useEffect(async () => {// Animate to 100 with the ref API.await ref.current.start({ x: 100 })// Animate to 0 with the function API.await animate({ x: 0 })}, [])
Compatible props
In the last example, only the onRest prop is inherited, because only some
props can have a default value.
The following props can have default values:
cancelconfigimmediateonChangeonDelayEndonPropsonRestonStartpause
The default object
In addition to passing default: true, you can also pass an object instead.
Only the props defined in your default object will be saved for future updates.
In this example, we are setting true as the default value of immediate. This
affects all future animations owned by the useSpring call, whether or not they
are declarative or imperative.
const { x } = useSpring({x: 0,default: { immediate: true },})useEffect(() => {// This will be immediate.x.start(100)})
Event props
Props like onRest can now be defined on a per-key basis.
useSpring({from: { x: 0, y: 0 },onRest: {x: () => console.log('x.onRest'),y: () => console.log('y.onRest'),},})
batchedUpdates support
Whenever an event prop is called, it's first wrapped with unstable_batchedUpdates
(exported by React), which ensures multiple setState calls won't cause components
to rerender more times than is necessary. This prevents several tricky edge cases.
React to finished delays
The onDelayEnd prop is called for every non-cancelled update, after its
delay is finished, but before the props are merged. It receives a props object
and the affected SpringValue object.
useSpring({onDelayEnd(props, spring) {console.log('onDelayEnd:', props, spring)},})
Inspect your props
The onProps prop is called for every update that isn't prevented.
It receives a props object and the affected SpringValue object.
It can be useful when debugging an animation.
The onProps prop is called immediately after all props have been merged into
the animation of the affected SpringValue object, but before the first
frame of an animation initiated by the props.
useSpring({onProps(props, spring) {console.log('onProps:', {// Inspect the update props.props,// Inspect the animated value.spring,// Inspect the animation state....spring.animation,})},})
The SpringContext component
Modify animations within the given children, but only the animations created
by the hook API (eg: useSpring) or renderprops API (eg: <Spring>) are affected.
Animations created with new SpringValue() or new Controller() are unaffected.
<SpringContext cancel={true}><App /></SpringContext>
The following props are supported:
cancel?: booleanconfig?: SpringConfigimmediate?: booleanpause?: boolean
The SpringContext component can be used anywhere.
The most common use case is pausing or finishing animations on a page while it is temporarily invisible to the user. For example, the user may navigate away from a modal with animated content that stays mounted when hidden.
Nested context
Descendants of a SpringContext component can use SpringContext to selectively
override any props forced by another SpringContext.
In the example below, only Page2 can play its animations. Pretend this element
tree is rendered more dynamically, and this code is a static representation.
<SpringContext pause={true}><Page1 /><SpringContext pause={false}><Page2 /></SpringContext><Page3 /></SpringContext>
Every nested SpringContext inherits the values of the nearest SpringContext
ancestor.
The skipAnimation global
When true, all animations are finished immediately. The requestAnimationFrame
loop is skipped entirely. Any onRest callbacks are still invoked.
import { Globals } from 'react-spring'Globals.assign({ skipAnimation: true })
Combine skipAnimation with prefers-reduced-motion to gracefully disable or
reduce the motion of your animations. The react-reduce-motion package can help with that.
import { Globals } from 'react-spring'import { useReduceMotion } from 'react-reduce-motion'const prefersReducedMotion = useReduceMotion()useEffect(() => {Globals.assign({skipAnimation: prefersReducedMotion,})}, [prefersReducedMotion])
The SpringValue class
Imperative APIs are a great way of animating in response to events, without
the need for component state (ie: render-based updates). In this sense, the
SpringValue class is a welcome addition to react-spring.
It wraps around an Animated node, the type of object you passed to animated
components in v8. Now you'll be passing SpringValue objects, which manage
their own Animation state (separate from any Controller object).
new SpringValue(props)
Create an animated value. Any type is valid, but only certain types are actually animated.
The props argument can be either..
- a
tovalue - or a set of
useSpringprops.
Types that cannot be animated are basically immediate: true animations.
Such types include: a boolean, a display string like "none", etc.
The animatable types are:
numbersnew SpringValue(0)strings with numbersnew SpringValue('0px 10px')new SpringValue('rgba(0, 0, 255, 1)')named colors
new SpringValue('red')and arrays of the preceding types
new SpringValue([0, '1em'])
Methods
get(): T
Get the current value.
const value = spring.get()
set(value): this
Set the current value, replace the goal value, and stop animating.
spring.set(1)
update(props): this
Add an update to the queue array. All updates in the queue are
applied at the same time.
const delays = [0, 100, 300]for (const delay of delays) {spring.update({ x: delay })}spring.start()
⚠️ This method may be deprecated soon.
start(): Promise
Process the queue array and replace it with an empty array.
The returned Promise resolves after every animation triggered by
the queue array has either finished or been cancelled.
await spring.start()
The awaited value is an AnimationResult object.
The AnimationResult type has the following properties:
value: Tspring: SpringValuefinished?: booleancancelled?: boolean
start(props): Promise
Process the given props object.
The returned promise is resolved after the animation is finished or cancelled.
If a start call triggers no animation, the promise resolves immediately.
await spring.start({ from: 0, to: 1 })
start(queue): Promise
Process the given array of updates.
The returned promise resolves to an AnimationResult object whose:
finishedproperty is onlytruewhen every animation triggered by thequeuewas finished before being stopped or cancelled.cancelledproperty is onlyfalsewhen all updates in thequeuewere never cancelled.
await spring.start([{ from: 0 },{ to: 10 },{ to: 20, delay: 1000 },])
finish(): this
Jump to the goal value immediately.
The onStart prop is called whenever finish is called before the first
frame. The onRest prop is called with finished: true as expected.
spring.finish()
finish(value): this
Jump to the given value immediately, and call the onStart and onRest
props the same way .finish() does.
spring.finish(100)
Fluid values (eg: other SpringValue objects) can also be passed.
stop(): this
Stop animating and cancel any delayed updates.
The goal value is replaced with the current value.
spring.stop()
reset(): this
Restart the animation using its cached from and to values.
spring.reset()
Equivalent to a .start({ reset: true }) call.
advance(dt: number): boolean
Advance the current animation by the given dt (in milliseconds).
You will probably never call this.
// Advance a single frame.spring.advance(1000 / 60)
is(phase: string): void
Check for the given phase.
if (spring.is('PAUSED')) {return}
Valid phases include:
CREATEDThe spring has never animated since being created.IDLEThe spring is done animating.ACTIVEThe spring is animating.PAUSEDThe spring was animating but is now paused.DISPOSEDThe spring can no longer animate.
Properties
key?: string
The name given to the SpringValue object by its owner.
For Controller objects, the key of each SpringValue is the property name
from either the to or from object prop.
When defined, the to prop can be an object with this key defined as
a property.
spring.key = 'foo'spring.start({ to: { foo: '180deg' } })
get idle(): boolean
Returns true when both:
- the current phase is anything but
ACTIVE - no async
toprop is active
Put simply, it's true when not animating.
get goal(): T
The end value of the current animation.
get velocity(): number | number[]
The velocity of the current animation.
animation: Animation
The animation descriptor.
queue?: SpringUpdate<T>[]
The queue of pending updates.
Call .start() to flush the queue.