
The will-change Property: When to Use It and When to Avoid It
will-change has a reputation as a magic performance switch. Add will-change: transform to something, the thinking goes, and it animates smoothly. Sometimes that's true. Just as often it does nothing useful, wastes GPU memory, or subtly changes how the element is positioned and layered. Some sites apply it to dozens of elements "just in case" and end up slower than if they'd never used it.
This guide explains what will-change actually tells the browser, when it helps, when it hurts, and the patterns that let you get the benefit without the side effects.
What will-change Does
will-change is a hint. It tells the browser: this property on this element is about to change, so prepare for it ahead of time.
.modal {
will-change: transform, opacity;
}
What "prepare" means is up to the browser. The most common optimization is layer promotion. The browser gives the element its own compositor layer, a separately painted surface that the GPU can move, scale, or fade without repainting it or the content around it.
Without a hint, the browser typically promotes an element when an animation on transform or opacity starts. That promotion involves painting the element into a new layer and uploading it to the GPU, which takes time. If it happens on the first frame of an animation, you can see a small hitch. With will-change, the layer is ready before the animation begins.
The Values It Accepts
will-change takes one of these:
auto: the default. No special hint.scroll-position: the element's scroll position is expected to change, so the browser may prepare content beyond the visible scroll area.contents: the element's contents are expected to change, so the browser should avoid caching them too aggressively.- One or more property names, such as
transform,opacity,top, orfilter.
.carousel-track {
will-change: transform;
}
.chat-log {
will-change: scroll-position;
}
.live-ticker {
will-change: contents;
}
In practice, transform and opacity are by far the most useful values, because those are the properties browsers can animate on the compositor. Hinting a layout property like width or top doesn't make it cheap to animate; it still triggers layout on every frame.
Side Effects You Need to Know About
will-change isn't just a hint to the rendering engine. For certain properties, it changes how the element behaves, immediately, as if that property already had a non-initial value.
It Creates a Stacking Context
will-change: transform or will-change: opacity creates a new stacking context, just as transform: translateZ(0) or opacity: 0.99 would. Children with z-index are now layered within the element instead of against the rest of the page. A dropdown inside a card with will-change: transform might suddenly appear under a sibling card instead of over it.
It Creates a Containing Block for Fixed Elements
will-change: transform (and filter, perspective, and a few others) makes the element a containing block for position: fixed descendants. A fixed-position toolbar or modal inside it will be positioned relative to the element instead of the viewport, and will scroll with it.
<div class="animated-panel">
<!-- This "fixed" banner is now fixed to .animated-panel, not the viewport -->
<div class="cookie-banner">We use cookies.</div>
</div>
.animated-panel {
will-change: transform;
}
.cookie-banner {
position: fixed;
inset-inline: 0;
bottom: 0;
}
If you've ever had a fixed element mysteriously stop being fixed, look for transform, filter, or will-change on an ancestor.
It Can Change Text Rendering
Promoted layers are sometimes rasterized differently. In some browsers and situations, text on a promoted layer looks slightly blurrier or loses subpixel antialiasing. This is most noticeable on scaled layers and on low-DPI screens.
Why Overusing It Hurts
Every compositor layer uses memory. A layer the size of a full-screen hero on a high-density display can take several megabytes of GPU memory. Promote dozens of large elements and you can exhaust the memory budget on a phone, at which point the browser has to fall back to slower paths, or in bad cases the tab crashes.
Layers also cost time to manage. The browser has to track them, composite them in the right order every frame, and repaint each one when its contents change. More layers is not automatically faster.
This is the anti-pattern to avoid:
/* Don't do this */
* {
will-change: transform;
}
.card,
.button,
.nav-item,
.avatar,
.icon {
will-change: transform, opacity;
}
Browsers already try hard to optimize. When you apply will-change everywhere, you remove their ability to make good decisions, and you pay the memory cost for elements that will never move.
When will-change Actually Helps
Use it when all of these are true:
- You've seen a real problem. A profile shows a stutter at the start of an animation, or repeated repaints during it.
- The animation uses compositor-friendly properties, mainly
transformandopacity. - You know when the animation will happen, so you can apply the hint shortly before and remove it afterward.
Classic good candidates:
- A slide-out drawer or off-canvas menu that opens on click.
- A modal that scales in.
- A carousel track that moves with swipe gestures.
- A large element animated repeatedly during a drag interaction.
Pattern 1: Apply It Just Before the Change
The best time to set will-change is right before the animation, not on page load. A reliable CSS-only trick is to apply it when the user shows intent, like hovering over a parent:
.gallery {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 1rem;
}
/* Prepare cards once the pointer enters the gallery */
.gallery:hover .gallery-card {
will-change: transform;
}
.gallery-card {
transition: transform 250ms ease;
}
.gallery-card:hover {
transform: scale(1.04);
}
Hovering the gallery gives the browser a moment to promote the cards before any of them is hovered directly. When the pointer leaves the gallery, the hint goes away and the layers can be released. Note that applying will-change on the element's own :hover is usually too late: the animation starts on the same frame.
Pattern 2: Toggle It with JavaScript
For click-triggered animations, set the hint on a preceding event and remove it when the animation ends:
<button class="menu-toggle" aria-expanded="false" aria-controls="drawer">
Menu
</button>
<nav class="drawer" id="drawer">
<!-- links -->
</nav>
.drawer {
position: fixed;
inset-block: 0;
left: 0;
width: min(320px, 85vw);
transform: translateX(-100%);
transition: transform 300ms ease;
}
.drawer.is-open {
transform: translateX(0);
}
const toggle = document.querySelector(".menu-toggle");
const drawer = document.querySelector(".drawer");
// Prepare on intent: pointer approaching the button or keyboard focus
const prepare = () => {
drawer.style.willChange = "transform";
};
toggle.addEventListener("pointerenter", prepare);
toggle.addEventListener("focus", prepare);
toggle.addEventListener("click", () => {
const open = drawer.classList.toggle("is-open");
toggle.setAttribute("aria-expanded", String(open));
});
// Release the layer once the transition finishes
drawer.addEventListener("transitionend", (event) => {
if (event.propertyName === "transform") {
drawer.style.willChange = "auto";
}
});
Touch devices don't fire pointerenter well in advance of a tap, so on mobile the hint arrives close to the click. That's fine. The animation still works, you just don't get the head start.
Pattern 3: Keep It On for Constantly Animated Elements
If an element animates continuously or is interacted with frequently, such as a carousel track during active use or a game canvas, leaving will-change in the stylesheet is reasonable. The browser would keep it promoted anyway.
.slider-track {
display: flex;
will-change: transform;
}
The rule of thumb: if it moves most of the time the page is open, a permanent hint is fine. If it moves occasionally, toggle it.
Pattern 4: Hint Scroll Containers Sparingly
will-change: scroll-position can help a scrollable panel with heavy content, like a chat log or a long table inside a fixed-height container. Like any hint, use it on specific containers you've profiled, not on every scrolling element.
How to Check if It's Working
DevTools can show you whether a hint is doing anything:
- Open Rendering in DevTools (in the More tools menu of Chromium-based browsers) and enable Paint flashing and Layer borders.
- Trigger your animation. Green flashes mean repaints. If the moving element flashes on every frame, it isn't being composited.
- Open the Layers panel to see every compositor layer, its memory estimate, and the reason it was created. Look for layers created because of
will-changethat you didn't expect. - Record in the Performance panel with CPU throttling and compare frame times with and without the hint.
If the numbers don't change, remove the hint. It isn't free.
Alternatives to Consider First
Before reaching for will-change, check whether the real problem lies elsewhere:
- Are you animating layout properties? Switching from
leftorwidthtotransformusually matters far more than any hint. - Is the element huge? Animating a small inner element rather than a full-screen wrapper reduces paint and memory cost.
- Is JavaScript blocking the main thread? Long tasks cause jank that
will-changecan't fix. - Is something repainting during the animation? A large
box-shadoworfilter: blur()on a moving element can be the real cost.
Quick Checklist
- Use
will-changeonly after profiling shows a problem. - Prefer
transformandopacityas hinted values. - Apply it shortly before the change and remove it afterward, unless the element animates constantly.
- Never apply it globally or to large groups of elements.
- Remember it creates a stacking context and can become a containing block for fixed descendants.
- Verify with the Layers panel and paint flashing that it actually helped.
Browser Support
will-change is supported in all modern browsers and has been for years. Browsers that ignore it simply behave as if it isn't there, so there's no fallback to write. What varies between engines is how they act on the hint, which is another reason to measure on the devices your users actually have.
Conclusion
will-change is a precise tool, not a general speed boost. It lets you tell the browser that a specific element is about to animate, so it can prepare a compositor layer before the first frame. Used on the right element at the right time, it removes the hitch at the start of an animation. Used everywhere, it wastes memory, creates stacking contexts you didn't intend, and can turn fixed elements into scrolling ones.
Treat it as a last-mile optimization: animate transform and opacity first, profile, and add the hint only where the numbers say it helps, ideally just before the change and removed right after.


