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.

// v8
import { useSpring } from 'react-spring'
import { Spring } from 'react-spring/renderprops'

The new version unifies these APIs into a single module!

// v9
import { 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 })
// v8
import { to } from 'react-spring'
const transform = to([x, y], (x, y) => `translate(${x}px, ${y}px)`)
return <a.div style={{ transform }} />
// v9
return <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 key
cancel: 'x',
// Using an array of keys
cancel: ['x', 'y'],
// Using a function
cancel: 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 a SpringValue or Controller).
  • 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, and friction is derived from tension and damping.

  • damping?: number

    The damping ratio, which dictates how the spring slows down.

    Set to 0 to never slow down. Set to 1 to slow down without bouncing. Between 0 and 1 is for you to explore.

    Only works when frequency is defined. Defaults to 1.

  • round?: number

    While animating, round to the nearest multiple of this number. The from and to values are never rounded, as well as any value passed to the set 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, setting bounce to 0.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 to 0.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:

  • numbers

    new SpringValue(0)
  • strings with numbers

    new 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 only true when every animation triggered by the queue was finished before being stopped or cancelled.

  • cancelled property is only false when all updates in the queue 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.