mrkeyoor.com_
Sat 08 Aug 20:58 UTC
npmWeb Frontendupdated 08 Aug 2026

@react-spring/web

@react-spring/web is the browser target of React Spring, a React animation library built around spring physics rather than fixed timelines. Its hooks produce animated values, and its animated HTML and SVG components update those values without sending every frame through React rendering. It handles single-property motion, coordinated lists, enter and leave transitions, gesture-driven values, scroll-linked effects, and imperative animation control.

Verdict

Choose @react-spring/web for interruptible, interaction-led React motion where spring behavior earns its API and bundle cost. For ordinary fades, exact design-tool timelines, or non-React pages, a smaller CSS or Web Animations approach is easier to own.

API stability4/5Version 10 keeps the long-established animated component, useSpring, useSprings, useTrail, and useTransition concepts, and its peer range spans React 16.8 through 19. The overloads and configuration shapes are broad, though, and the monorepo's default branch is named next, so major-version upgrades deserve real interaction and TypeScript testing instead of a blind dependency bump.
Docs4/5The official site documents each hook, spring configuration, events, TypeScript types, reduced motion, testing, and many live examples. It is unusually candid about testing delays and Jest module resolution. Some reference material is generated into tables that is less useful in raw source, and similar hooks have subtly different return shapes that require careful reading.
Maintenance5/5The repository was pushed on 2026-08-08, the same day as this snapshot, and npm shows 10.1.2 published on 2026-06-24. The project supports React 19 and maintains separate web, native, and three targets. GitHub reports 79 open issues and pull requests, a manageable working queue for a mature animation project rather than evidence of abandonment.
Ecosystem5/5The package recorded 5,564,444 npm downloads for the week ending 2026-08-06, and the shared repository has 29,132 stars. Target packages cover React DOM, React Native, and React Three Fiber, while the web package publishes both import and require entries plus bundled declarations. Examples and integrations across the React community are plentiful.

Use it if

  • You want motion that can change destination mid-flight without hand-tuning a new CSS keyframe timeline
  • You need enter and leave animation for dynamic React lists or dialogs through useTransition
  • You are building drag, hover, or scroll interactions where animated values must follow continuously changing input
  • You want one hook-based API for HTML elements, SVG attributes, numeric values, colors, and transform strings
Skip it if

Setup reality

Install @react-spring/web alongside React and React DOM. Version 10.1.2 accepts React 16.8 through 19, so most current React applications satisfy the peers, and the package ships both ESM and CommonJS entries with TypeScript declarations. The conceptual setup is the bigger cost. Values returned by useSpring are SpringValue objects, not plain numbers, and they belong on animated.div or another animated component. Reading them during render or putting them on a normal DOM element produces confusing results. The object form of useSpring returns only styles, while the function form with a dependency array returns [styles, api]; that overload catches people who copy an imperative example into declarative code. List removal needs useTransition because a component cannot animate after React has already unmounted it. Spring tuning uses mass, tension, and friction, not a duration by default, and easing only applies when duration is configured. Respect reduced-motion preferences with useReducedMotion near the app root, since it changes a global skipAnimation setting. Tests also need scheduling work: the official Jest recipe sets Globals.assign({ skipAnimation: true }) and then waits a tick or advances fake timers. The documentation additionally calls out a Jest ESM resolution workaround for older configurations. In Next.js or another React Server Components framework, the hook-using component must be a client component because it uses React state, effects, and browser animation frames.

Patterns

Animate a value from React stateanimate-state-change

import { animated, useSpring } from '@react-spring/web'

function Panel({ open }) {
  const styles = useSpring({
    opacity: open ? 1 : 0,
    y: open ? 0 : 16,
  })

  return <animated.div style={styles}>Settings</animated.div>
}

Put spring styles on an animated element. A normal div does not know how to subscribe to SpringValue updates.

Start an animation from an eventcontrol-imperatively

import { animated, useSpring } from '@react-spring/web'

function NudgeButton() {
  const [styles, api] = useSpring(() => ({ x: 0 }), [])

  return (
    <animated.button
      style={styles}
      onClick={() => api.start({ from: { x: 0 }, to: { x: 20 } })}
    >
      Nudge
    </animated.button>
  )
}

The [styles, api] tuple comes from the function overload with dependencies. Passing a plain object returns styles only.

Tune mass, tension, and frictiontune-spring-physics

const styles = useSpring({
  from: { scale: 0.9, opacity: 0 },
  to: { scale: 1, opacity: 1 },
  config: { mass: 1, tension: 210, friction: 20 },
})

return <animated.div style={styles}>Ready</animated.div>

Springs have no fixed duration by default. If the specification requires an exact time, set config.duration and treat it as a duration animation.

Give each animated property different physicsconfigure-per-property

const styles = useSpring({
  from: { opacity: 0, y: 30 },
  to: { opacity: 1, y: 0 },
  config: key =>
    key === 'opacity'
      ? { duration: 180 }
      : { tension: 220, friction: 22 },
})

Easing has an effect only with duration. A config function receives the animated property key and can mix duration and spring behavior.

Animate list items entering and leavingtransition-list-items

import { animated, useTransition } from '@react-spring/web'

const transitions = useTransition(items, {
  keys: item => item.id,
  from: { opacity: 0, height: 0 },
  enter: { opacity: 1, height: 40 },
  leave: { opacity: 0, height: 0 },
})

return transitions((style, item) => (
  <animated.div style={style}>{item.label}</animated.div>
))

Provide stable keys for object lists. useTransition retains leaving items long enough to animate them out of the DOM.

Create one spring per itemanimate-many-items

import { animated, useSprings } from '@react-spring/web'

const springs = useSprings(people.length, people.map((person, index) => ({
  from: { opacity: 0, y: 12 },
  to: { opacity: 1, y: 0 },
  delay: index * 40,
})))

return springs.map((style, index) => (
  <animated.div key={people[index].id} style={style}>
    {people[index].name}
  </animated.div>
))

The count and configuration list must stay aligned. For items that are actually added and removed, useTransition tracks lifecycle more safely.

Make following items trail the firstcreate-trailing-sequence

import { animated, useTrail } from '@react-spring/web'

const trail = useTrail(labels.length, {
  from: { opacity: 0, x: -12 },
  to: { opacity: 1, x: 0 },
})

return trail.map((style, index) => (
  <animated.span key={labels[index]} style={style}>
    {labels[index]}
  </animated.span>
))

useTrail couples each spring to the one before it. Use useSprings when every item needs independent timing or destinations.

Interpolate one spring into a CSS transformderive-transform-value

const { progress } = useSpring({
  from: { progress: 0 },
  to: { progress: 1 },
})

return (
  <animated.div
    style={{ transform: progress.to(p => `scale(${0.8 + p * 0.2})`) }}
  />
)

Use SpringValue.to for derived display values. Calling get during render reads a snapshot and does not subscribe the element to future frames.

React when an animation settlesrun-rest-callback

const styles = useSpring({
  opacity: visible ? 1 : 0,
  onRest: result => {
    if (result.finished && !result.cancelled) {
      console.log('animation settled')
    }
  },
})

onRest can run after interruption as well as completion. Check the result flags before treating it as a completed workflow step.

Honor the operating-system motion preferencerespect-reduced-motion

import { useReducedMotion } from '@react-spring/web'

function App() {
  useReducedMotion()
  return <Routes />
}

The hook synchronizes React Spring's global skipAnimation setting. Call it near the root so every controller follows the same preference.

Drive a progress bar from page scrollanimate-scroll-progress

import { animated, useScroll } from '@react-spring/web'

function ScrollProgress() {
  const { scrollYProgress } = useScroll()
  return (
    <animated.div
      style={{ scaleX: scrollYProgress, transformOrigin: 'left' }}
      className="progress"
    />
  )
}

useScroll reads browser scroll state, so this component belongs on the client in frameworks with server components.

Make animation tests deterministicdisable-animation-in-tests

import { Globals } from '@react-spring/web'

beforeEach(() => {
  Globals.assign({ skipAnimation: true })
})

afterEach(() => {
  Globals.assign({ skipAnimation: false })
})

Skipping animation still schedules the React update. Await Testing Library waitFor or advance fake timers before asserting the final style.

Alternatives

PackageRegistryPick it when
framer-motionnpmYou want a larger but more declarative animation system with layout animation, gestures, and variants
react-transition-groupnpmYou mainly need enter and exit lifecycle states and prefer to write the CSS yourself
@motionone/reactnpmYou want a compact Web Animations API based React layer for duration-driven motion
gsapnpmYou need timeline-heavy, framework-independent animation with precise sequencing