Skip to content
Performance

Will-change is a promise — keep it or break it

The will-change property tells the browser to prepare a compositor layer before it is needed. Useful, yes. But every promise has a cost: layer memory, and the jank you create when you break the promise by never using the layer.

Advanced2 min read (computed · recorded 10)updated 2026-09-10performancecsscompositingwill-change

What the property actually does

will-change: transform tells the browser 'this element is about to animate its transform — promote it to its own compositor layer now, so the animation does not have to do the promotion on the first frame.' That first-frame promotion is exactly where stutter comes from, so the promise removes it.

The cost: a compositor layer is a texture in GPU memory. Ten elements with will-change are ten textures. A hundred — a scroll-linked hero, a staggered grid, every card in a bento — and you have traded first-frame stutter for a memory bill and, ironically, slower scrolling as the GPU fights to keep all those layers alive.

The broken promise

The classic bug is will-change: transform on a hover target that never animates, or left on after the animation ends. The browser keeps the layer alive indefinitely, and the page quietly pays for layers that do nothing. will-change is a promise: when the animation finishes, you must revoke it — or the browser keeps reserving the table for a guest who left.

The rules that keep the promise honest

  • Apply will-change in JavaScript right before the animation starts, and remove it in the animationend / finished handler. CSS-only: use it on the :hover state or a class that toggles with the animation.
  • Limit it to transform and opacity — the two properties that animate on the compositor. will-change: all is a panic attack, not a strategy.
  • Never apply it to more than a handful of elements. If you need dozens of layers, the problem is the layout, not the promotion.
  • Never put it on a resting state that does not animate — that is the promise with no event.
  • Prefer the browser's own judgment for one-off entrances: most modern engines promote at animation start fast enough that will-change is only needed for long or heavy animations.
honest-will-change.ts
el.addEventListener("mouseenter", () => {
  el.style.willChange = "transform";      // promise made
  el.animate([{ transform: "scale(1.06)" }], { duration: 180 });
});
el.addEventListener("animationend", () => {
  el.style.willChange = "auto";           // promise kept & released
});

When it genuinely pays

Three cases earn their keep. Long-running animations: a drifting aurora band or a continuous marquee that will animate for seconds, where a missed frame at any point is visible. Scroll-linked effects: an element that must already be a layer when the scroll handler starts writing to it. And many elements transforming simultaneously — a full grid stagger — where the browser would otherwise promote fifty layers in one frame.

Everywhere else, measure first. If the entrance is one card, let the browser do its job. will-change is an optimization you apply to a problem you have measured, not a garnish you sprinkle for luck.

Practise the lesson

Theory sticks when you ship it. These original Motif assets put this guide's lesson to work — open one and copy it into your own page.