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
,z
translate
,translateX
,translateY
,translate3d
rotate
,rotateX
,rotateY
,rotate3d
scale
,scaleX
,scaleY
,scale3d
skew
,skewX
,skewY
matrix
,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: any
The current value when the animation ended.target: object
The object being animated (either aSpringValue
orController
).finished?: boolean
Equals true when the animation finished before being stopped or cancelled.cancelled?: boolean
Equals 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?: number
The natural frequency (in seconds), which dictates the number of bounces per second when no damping exists.
When defined,
tension
is derived from this, andfriction
is derived fromtension
anddamping
.damping?: number
The damping ratio, which dictates how the spring slows down.
Set to
0
to never slow down. Set to1
to slow down without bouncing. Between0
and1
is for you to explore.Only works when
frequency
is defined. Defaults to1
.round?: number
While animating, round to the nearest multiple of this number. The
from
andto
values are never rounded, as well as any value passed to theset
method of an animated value.bounce?: number
When above zero, the spring will bounce instead of overshooting when exceeding its goal value.
Its velocity is multiplied by
-1 + bounce
whenever its current value equals or exceeds its goal. For example, settingbounce
to0.5
chops the velocity in half on each bounce, in addition to any friction.progress?: number
When 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.5
means "start from the middle of the easing function". Any number>= 0
and<= 1
makes sense here.restVelocity?: number
The smallest velocity before the animation is considered to be "not moving". When undefined,
precision
is 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:
cancel
config
immediate
onChange
onDelayEnd
onProps
onRest
onStart
pause
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?: boolean
config?: SpringConfig
immediate?: boolean
pause?: 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
to
value - or a set of
useSpring
props.
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:
number
snew SpringValue(0)string
s 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: T
spring: SpringValue
finished?: boolean
cancelled?: 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:
finished
property is onlytrue
when every animation triggered by thequeue
was finished before being stopped or cancelled.cancelled
property is onlyfalse
when all updates in thequeue
were 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:
CREATED
The spring has never animated since being created.IDLE
The spring is done animating.ACTIVE
The spring is animating.PAUSED
The spring was animating but is now paused.DISPOSED
The 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
to
prop 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.