ESC

Type to search the knowledge base.

will-change Pitfalls

will-change hints the browser to prepare layers — use sparingly, set only before animation, and remove after to avoid memory bloat.

advanced3 min read
  • css
  • will-change
  • performance

will-change tells the browser which properties are likely to change, allowing optimizations (often layer promotion) ahead of time. Used well, it prevents first-frame jank. Used as a permanent will-change: transform on every card, it burns memory and can make performance worse.

Docs: MDN will-change, web.dev stick to compositor properties.

Syntax

.drawer {
  will-change: transform;
}

.fader {
  will-change: opacity;
}

.box {
  will-change: transform, opacity;
}

Valid values include specific properties, scroll-position, and contents (rarely needed). Don’t use will-change: all.

The right lifecycle

  1. Default: no will-change.
  2. Just before an interaction/animation, set it (class or JS).
  3. Animate transform/opacity.
  4. On transitionend / animationend, remove it.
.card {
  transition: transform 200ms ease;
}

.card.is-animating {
  will-change: transform;
}

.card.is-raised {
  transform: translateY(-4px);
}
el.classList.add("is-animating");
requestAnimationFrame(() => el.classList.add("is-raised"));
el.addEventListener(
  "transitionend",
  () => el.classList.remove("is-animating"),
  { once: true }
);

Why permanent will-change hurts

  • Extra compositor layers consume GPU/CPU memory.
  • More layers can increase composite time.
  • Stacking contexts change — unexpected overlay bugs.
  • Mobile browsers are less forgiving than your MBP.
/* Anti-pattern */
* {
  will-change: transform;
}

When you might not need it

Modern browsers often promote layers when a transform/opacity transition starts. If profiling shows no first-frame hitch, skip will-change. Prefer fixing “animating left/width” over sprinkling hints.

Alternatives and relatives

Tool Role
will-change Hint upcoming change
transform: translateZ(0) Old force-layer hack — still costly if overused
contain Layout/paint containment — different problem
Content visibility Skip rendering offscreen content

Interview out-loud

“will-change optimizes for known upcoming changes by letting the browser prepare. I apply it narrowly and temporarily around animations, never globally. If everything is will-changed, nothing is optimized — memory and composite costs climb. Profiling beats cargo-cult layer promotion.”

Footguns

  1. Shipping will-change in resting component CSS forever.
  2. will-change: all.
  3. Hinting properties you don’t actually change.
  4. Using it to “fix” layout thrash from bad animation properties.
  5. Ignoring stacking-context side effects on dropdowns.

When profiling says you need it

Symptoms: first frame of a transform transition stutters once, then is smooth. That can mean late layer promotion. Applying will-change: transform on hover intent (or 100ms before open) may help.

.menu-trigger:hover + .menu,
.menu-trigger:focus-visible + .menu {
  will-change: transform, opacity;
}
.menu {
  transition: transform 160ms ease, opacity 160ms ease;
}

Remove after close. On infinite lists, never leave will-change on hundreds of rows — recycle sparingly on the active item only.

Memory intuition

Each promoted layer stores bitmaps. Full-screen layers are expensive. Prefer animating a small drawer layer rather than promoting the entire page chrome. If DevTools layer count climbs steadily with scroll, something is promoting too aggressively.

Code review red flags

  • will-change in a base class applied to every card
  • will-change: all
  • will-change without a removal path
  • Using it to “optimize” animations of height or top

Reject those patterns. Prefer correct animation properties first. Treat will-change like a temporary performance scalpel, not a permanent decoration.

Further reading

Related guides