This page looks best with JavaScript enabled

Improve Animation Performance in React

 ·  ☕ 7 min read  ·  ✍️ Iskander Samatov

improve-react-animation-performance


Smooth animations immediately make any website stand out and make it more inviting. That is why fluid animations and interactions can be one of your primary tools for improving the UX of any application.

Developers often put lots of effort into reducing the initial page load time, tuning it to shave off a few extra milliseconds. Yet they forget to invest in their website’s interactions which can be just as beneficial.

In this post, we’ll work on building an understanding of what it takes to create animations that are smooth and performant. We will also cover what tools you should use to tune and analyze animations in your React application.

While this post is written with React in mind, the lessons can apply to any JavaScript app.

FPS and Why it Matters for React Applications

The most important metric to keep in mind when testing web animations is FPS.

FPS, or frames per second, is a measure of the speed at which an output device displays consecutive images. It’s a measurement of how smooth any given interaction feels.

For animations, this number needs to be high enough to create the illusion of motion by displaying a series of still images at a fast-enough pace. If the FPS is too low, the animation will seem choppy to the human eye.

60 FPS is the refresh rate that most displays have. 60FPS is the ideal target for smooth scrolling and will enable a more compelling user experience.

If you break it down, with 1000 milliseconds / 60 frames per second, each frame only has 16.6 milliseconds to render - not a lot of time. That is why achieving 60 FPS is not always easy!

If you can’t reach 60 FPS, aim for 30 FPS instead. 30 FPS will still make interactions smooth as long they stay consistent. Consistent 30 FPS beats inconsistent 60 FPS. With that said, at 15 FPS or below, any animation becomes noticeably jerky.

All in all, FPS can play a significant role in the user engagement level of your application.

Optimizing your Animation Performance

Pixel pipeline

When working with animations or transitions, it’s good to understand the pixel pipeline.

Pixel pipeline describes the five steps a browser takes during each rendered frame, which are:

  • Running JavaScript
  • Changing CSS reflow properties
  • Recalculating element geometry based on the reflow properties
  • Rendering elements in layers (painting)
  • Ordering layers correctly on the screen (composing)

pixel-pipeline



Understanding the order of the pixel pipeline operations and which changes to the DOM trigger which operations are crucial. It’s also worth noting that some operations are less expensive than others. For example, composing is much faster to perform than painting.

Now let’s get to animations: there are two ways you can animate elements: CSS (declarative) or JavaScript (imperative). As with many things, which method to pick will depend on what works best in your situation.

CSS animations

Generally speaking, CSS animations are more performant than JavaScript animations.

Not all CSS properties are made equal

As I mentioned earlier, composing is cheaper than pixel painting. That is why, when possible, you should use properties that trigger composing over the ones that trigger repainting. Properties such as opacity or transform are good examples of the former ones.

For similar reasons, when trying to hide elements, it’s more performant to use opacity over display or visibility. As a side note: when using opacity, you should also set pointer-events to none. Doing so ensures that a user doesn’t accidentally click on the hidden elements.

1
2
3
4
5
6
7
8
 .element {
     opacity: 1;
     transition: .3s;
}
 .element.closed {
     opacity: 0;
     pointer-events: none;
}

Promote element paint

But you can control more than just which CSS properties to use. You can also manually promote an element to its own compositor layer (remember our overview of composition in the pixel pipeline.) This forces elements to always be painted and ready:

1
.box { will-change: auto;}

This is the easy way to let the browser know which elements are paint- expensive and thus need more focused assistance. But try not to overdo it because the will-change optimizations can get heavy. And the browser already does its best to tune performance.

JavaScript animations

While JavaScript animations provide better control, they lack performance. They run on the main thread, which makes them more likely to drop frames or run them inconsistently.

Combine JavaScript with CSS

One way to combine both the control of JavaScript and the performance of CSS is by declaring your animations in CSS and triggering them by switching classes in JavaScript. Or, if you are working with newer browsers, the Web Animations API also allows you easily combine CSS and JavaScript animation code.

Use libraries

Use popular animation libraries instead of writing your own custom solution when possible. Those libraries are usually battle-tested and will do a better job optimizing performance than you could on your own. Some good examples of animation libraries for React are framer-motion and react-spring .

Use requestAnimationFrame

If you do end up writing your own code, use requestAnimationFrame to tune your animations. requestAnimationFrame is more performant than simply using setTimeout or setInterval. It tries to minimize the frame loss by grouping changes into a single repaint, which saves CPU cycles.

Use Intersection Observer

If your animation depends on elements entering or exiting the viewport, you can use the Intersection Observer API to track that. Doing so is a much better alternative to polluting your application’s execution thread with event handlers that manually calculate the position of affected elements using methods like Element.getBoundingClientRect() .

Avoid redundant reads

The order in which you perform read and write operations to the DOM can also affect the performance. By doing back-and-forth reading and writing you will cause redundant reflows that hinder the performance.

To avoid this, try to minimize the duplicate reads by storing the values:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
items.forEach((item) => {
  item.style.transform =
    "translateY(" + container.getBoundingClientRect().height + "px)";
});

 // VS

const containerHeight = container.getBoundingClientRect().height + "px";
items.forEach((item) => {
  item.style.transform = "translateY(" + containerHeight + "px)";
});

Throttle and debounce

Some DOM events, like scrolling, can fire quite often. So you might want to consider debouncing or throttling your callbacks to avoid triggering expensive operations too often.

Say we have an event that runs an animation as the user scrolls through the list. Instead of running the animation every time the user scrolls, you can throttle it at a reasonable interval that will still keep your animation running smoothly:

1
2
3
4
5
6
7
8
const throttleTime = 100;
function onScroll(event) {
  if (Date.now() > prevTime + throttleTime) {
    callback();
  }
}

window.addEventListener("scroll", onScroll);

Now the callback will only trigger once every 100 milliseconds because we are throttling it rather than calling it each time the event occurs.

Analyzing animation performance in React

Chrome developer tools

While knowing tricks for improving your application is necessary, it’s just as necessary to be able to troubleshoot performance issues. And one of the best tools for measuring web performance is Chrome developer tools.

Under the More Tools menu in the console, you will find the Rendering tool with several options, including tracking dirty elements, paint flashing, calculating FPS, highlighting layer borders, and others.

Chrome rendering tool React animation

Profiler

Now let’s talk about React-specific tools. The redundant rendering of the components can often affect animations and make certain interactions feel janky. React profiler can be used to analyze rendering and performance issues in React applications. React profiler is a part of the React Dev tools chrome extension. Official React docs have a nicely-written, comprehensive overview of React profiler.

Conclusion

And that’s all for this post. Animation is a powerful tool to improve user experience and drive engagement, but creating smooth and seamless interactions takes work. And when you see a website that feels smooth, you know the makers have put in the time to create an excellent experience for their visitors.

Hopefully, this post has given you some useful tips and tricks on how to do so in your apps.

If you’d like to get more web development, React and TypeScript tips consider following me on Twitter, where I share things as I learn them.
Happy coding!

Share on

Software Development Tutorials
WRITTEN BY
Iskander Samatov
The best up-to-date tutorials on React, JavaScript and web development.