May 27, 2026

4 min

Optimizing the performance of animation rendering on a page

Some animation techniques can tax the system more than others. For example, CSS properties such as margins, padding, positions (e.g. top, left) affect the size and position of elements, causing the browser to have to recalculate the page layout and repaint the elements in each animation frame. In contrast, other properties, such as opacity, transform and filter, do not require layout recalculation or repainting, making their animation less resource-intensive.

See it in the given example:

Before the browser displays the final page, there are several steps in the rendering process. In the example above, we can see that: - width, height, padding... cause all the steps to be recalculated - color, border-style, visibility... do not cause the layout to be recalculated - z-index, transform, opacity... do not cause either the layout or the painting to be recalculated

Localizing the problem

The first step was to use the “Paint” highlighting option in Chrome's developer tools.

After investigating the suspicious animations, we discovered something interesting. Our expectation was that the animation would only affect a specific area, and for some reason it was causing unnecessary “repaint” of other elements. It took us literally a few minutes to find other elements on our site exhibiting similar behavior, e.g.:The page header was designed in two versions: full and minimalist. The full version is displayed when the user first enters the site and when scrolling up the page. In other situations, when the user browses the content or scrolls down the page, the minimalist version is displayed. The transition animation between these versions has the effect of advancing additional header elements from the top, which adds fluidity and dynamism to the browsing experience.

This animation was originally implemented as follows:

What is wrong?

Implemented in this way, the animation is not optimal and has several significant drawbacks. The browser does unnecessary work, refreshing content that it should not. First, changing the spacing affects the layout of the entire component. After recalculating the layout, the browser has to refresh the parts of the page (repaint) that were affected by the margin change, if the margin change affects multiple components. Secondly, the use of transition: all is inefficient, because it causes the animation of all possible CSS properties of the component, even those that are not needed.Below we can see how many style recalculations per second there were and which elements were recalculated/refreshed using the original way. On average we had from 80 refreshes per second.

Get to know our approach to Consulting

Read more about Consulting Izometryczny rysunek techniczny trójwymiarowego sześcianu z widocznymi krawędziami i wewnętrzną strukturą siatki

Header animation optimization

Our goal was to avoid layout recalculation and element refresh steps as much as possible. You can read more about optimizing the page rendering here.

We started optimizing this step by step. We restricted the transition property to animate only the transform property, instead of using the generic all notation. Instead of animating the margin, we used the transform property (translate3d). Transforms are usually handled by the GPU, which improves the performance of painting and re-painting.Why, by using the transform property, we let the browser skip the layout recalculation and refresh steps. The browser simply moves the already painted layers, which is called composition. Rewritten version:

Zrzut ekranu DevTools z analizą wydajności: flame chart z wywołaniami funkcji, podświetlone Recalculate Style (1,28 ms, 59 elementów).

Promoting elements

The animated component must be on its own composition layer. If this is not the case, then to achieve this, you can try to use the will-change property that will promote our element. will-change: transform; This informs the browser that we anticipate a change in the transform property of the component, thereby increasing the likelihood that the element will be moved to a separate composition layer. Warning: Don't promote components unnecessarily.

Avoid excessive use of this rule. Layers require memory and management.

You can read more about the will-change property and managing the number of layers here and here

Results

After applying the new approach, the average number of layout and style conversions per second of page refreshes has decreased to 2-4 times per second. In addition, reflow and repaint are now performed only in those places where they are absolutely necessary.

It is worth noting that the header further contains components that trigger reflow and repaint. In our case, several icons had the opacity property set, so that changing the transition in the parent component forced the repaint of these icons.

Let's talk about potential areas of collaboration!

You can find more articles on this topic on our blog