will-change
will-change is a hint to the browser: "this property is about to change, plan ahead." It exists purely for performance — it changes nothing about how an element looks, only how the browser prepares to render it. Used well it removes a hitch at the start of an animation; used carelessly it burns memory and can make things slower.
What the hint actually does — layer promotion
Browsers render in layers composited together on the GPU. An element that changes via transform or opacity can often be updated by moving/fading its own layer, skipping layout and paint entirely (see Layer Promotion & Compositor). Creating that separate layer costs time, though — normally the browser only promotes an element to its own layer once an animation actually starts, causing a small stutter right at that moment. will-change tells the browser to do that promotion work early, before the animation begins, so the expensive part is already done by the time it matters.
.card {
will-change: transform;
}
.card:hover {
transform: translateY(-4px);
}When it helps
A large or complex element (heavy box-shadow, filters, lots of child content) that stutters on the very first frame of a hover/transition, but is smooth afterward — the classic sign the promotion cost is happening at the wrong time.
An element you know with certainty is about to animate very soon — for example, right before triggering a modal open transition.
Scroll-linked or drag-driven transforms, where the element updates every frame and benefits from staying on its own compositor layer for the whole interaction.
When it hurts — the memory cost
Every layer the browser creates consumes GPU memory, and a page with dozens of promoted layers can run out of that budget, forcing the browser to demote layers, merge them, or slow down elsewhere to compensate — the opposite of the intended effect. will-change applied broadly (every card in a grid, every list item) or left on permanently is the most common way this backfires.
Usage pattern | Effect |
|---|---|
Applied to one element, right before it animates, removed after | Fast — this is the intended pattern |
Applied to a whole list/grid of items "just in case" | Wastes memory on layers for elements that may never animate |
Left on permanently in a stylesheet | The element stays promoted forever, paying the memory cost with no matching benefit once the animation is over |
Applied to | Promotes on every property change — almost always the wrong choice; name the specific property instead |
will-change: all and never apply will-change to more elements than are about to actually animate — both defeat the purpose by asking the browser to pre-allocate resources it may never need.The correct usage pattern — add, then remove
The most reliable pattern applies will-change via JavaScript right before the animation starts and removes it right after it finishes, rather than leaving it in a static stylesheet rule.
const card = document.querySelector('.card');
card.addEventListener('mouseenter', () => {
card.style.willChange = 'transform';
});
card.addEventListener('transitionend', () => {
card.style.willChange = 'auto'; // release the layer once done
});will-change: transform on a small, bounded number of interactive elements (a handful of buttons or cards, not hundreds) is a reasonable, simpler compromise. Reserve the add/remove JS pattern for animations on many elements at once, or ones triggered programmatically rather than by hover.Naming the specific property
/* Good — tells the browser exactly what's coming */
.panel { will-change: transform, opacity; }
/* Avoid — over-broad, promotes for changes that don't need it */
.panel { will-change: all; }Alternatives — the old translateZ(0) hack
Before will-change existed, developers forced layer promotion with a no-op 3D transform — transform: translateZ(0) or translate3d(0,0,0) — which had the side effect of triggering GPU compositing. It works in every browser that supports 3D transforms, but it is a hack repurposing a visual property for a performance side effect, with no way to ever "undo" it short of removing the transform, and it can introduce subtle rendering differences (blurrier text, changed subpixel rounding) that will-change avoids.
will-change | translateZ(0) hack | |
|---|---|---|
Purpose-built for this | Yes | No — repurposes a visual transform |
Can be toggled off cleanly | Yes ( | Awkward — removing it means removing a real transform value |
Side effects on rendering | None | Can subtly affect text rendering/subpixel positioning |
Still relevant today | The correct modern choice | Legacy fallback for very old browser support only |
Measuring with DevTools layers
Chrome DevTools → More tools → Layers panel shows every compositor layer currently created, including why each one exists (a tooltip lists the promotion reason, e.g. "will-change: transform").
The Rendering tab has a "Layer borders" option that outlines every layer directly on the page — a quick visual check for whether you have far more layers than expected.
The Performance panel's frame timeline shows whether a given frame did Layout + Paint + Composite, or skipped straight to Composite — the direct evidence that a
will-change(or transform-only animation) is paying off.
will-change declarations left in a stylesheet from an earlier optimization pass — they are easy to add and easy to forget to remove.Related pages: Layer Promotion & Compositor, Repaints & Reflows, CSS Performance Overview, and Individual Transform Properties.