will-change

Hints the browser about upcoming property changes for optimization. Overuse degrades performance; reset with wca.

Class List

ClassCSSDescription
wcawill-change: autoDefault. Browser decides optimization automatically
wctwill-change: transformHint transform will change (GPU accel)
wcowill-change: opacityHint opacity will change
wcspwill-change: scroll-positionHint scroll-position will change
wccwill-change: contentsHint contents will change

Visual Demo — Why Nothing Changes

Both boxes scale identically on hover. Only the right one carries the hint class, and there is no visible difference at all, because this property does not change appearance: it tells the browser to get ready.

tall300msease

No hint

tall300msease wca

With the hint — no visible difference

The difference lives inside the browser, not on screen. A hinted element may be promoted to its own layer ahead of time so the first frame is smoother, and it holds on to that memory for as long as the hint stays.

Usage Examples

will-change creates a separate layer for GPU acceleration. Always reset after animations.

<!-- Atomic CSS supports will-change: auto (wca) —
     the browser default / reset value. -->

<!-- Reset will-change after an animation finishes -->
<div class="wca">
  Element with will-change reset to auto
</div>

<!-- Tip: for a specific hint like will-change: transform,
     the browser also auto-optimizes elements that already have
     a transition/animation, so tall200msease + hover-ts1-1
     already gets GPU acceleration without a will-change hint. -->
<div class="tall200msease hover-ts1-1">
  Card that scales up on hover (auto-optimized)
</div>

Class Details

wcawill-change: auto

The default value and, in practice, the off switch. It lets the browser decide, and it is what you set once an animation is done to drop the hint. It is also the value shipped as a real class in this set.

<!-- Drop the hint again once the animation is done -->
<div class="wca tall300msease">
  Back to letting the browser decide.
</div>

wctwill-change: transform

Announces that a transform is about to change. Add it just before a heavy movement such as a drawer opening or a carousel advancing, and remove it the moment the movement ends.

<!-- Panel that is about to slide in from the right -->
<aside class="wct tall300msease pf t0 r0 h100vh w30rem bg18181B">
  Remove the hint as soon as the slide finishes.
</aside>

wcowill-change: opacity

Announces that opacity is about to change. It is only worth it for large areas, such as a full-screen overlay fading in.

<!-- Full-screen overlay fading in: a large area of opacity -->
<div class="wco tall300msease o0 pf t0 l0 w100p h100vh bg0-0-0-50"></div>

wcspwill-change: scroll-position

Announces that the scroll position is about to change. Add it right before scrolling a long list programmatically and drop it once scrolling stops.

<!-- Long list about to be scrolled programmatically -->
<div class="wcsp oya h40rem">
  <p class="fs14px c71717A">Thousands of rows.</p>
</div>

wccwill-change: contents

Announces that the element content itself changes constantly. It stops the browser from caching a painted picture of the element, so reserve it for canvas-like regions that repaint every frame.

<!-- Region whose content is repainted every frame -->
<div class="wcc w100p h30rem bg18181B br8px">
  Live chart output.
</div>

Practical Example — Hint Only Just Before the Drawer Opens

Instead of baking the hint into the markup, it is added in code when the pointer reaches the open button and removed when the animation finishes. That is the only safe way to use this property.

<!-- Markup ships WITHOUT the hint -->
<button id="open-drawer" class="bg6366F1 cFFFFFF br6px py12px px16px fs14px">
  Open
</button>

<aside id="drawer" class="tall300msease pf t0 r0 h100vh w30rem bg18181B">
  <p class="p2rem fs14px cA1A1AA">Drawer contents</p>
</aside>

<!-- JS adds the hint on pointer approach, removes it after the animation:
     open.addEventListener('pointerenter', () => drawer.classList.add('wct'));
     drawer.addEventListener('transitionend', () => drawer.classList.remove('wct'));
     Leaving 'wct' on permanently would pin a compositor layer for the whole
     session, which costs memory and can slow the page down. -->

Tips / Caveats

Never leave it on permanently

A hinted element is promoted to its own layer and holds that memory for as long as the hint exists. Put it on every row of a list and you create hundreds of layers, which makes scrolling worse, not better. Add it only for the moment you need it and take it off right away.

Adding it at hover time is already too late

The whole point is to give the browser time to prepare. Adding it at the exact moment the animation starts leaves no time at all, so you get none of the benefit and pay the layer creation cost mid-animation.

Most transitions do not need it

Transform and opacity animations are already optimized by the browser. Reach for it as a last resort, after you have measured real dropped frames.

It creates a stacking context and reorders overlaps

A hinted element establishes a new stacking context. If a dropdown suddenly hides behind a sibling, or a fixed element gets clipped oddly, this property is a likely cause.

Hint the property you actually change

Hinting transform and then animating width and height wastes the preparation and still triggers layout. Keep the hint and the animated property in sync.

Do not overuse — causes performance degradation

Applying to all elements creates unnecessary layers, increasing memory and worsening performance.

Add before animation, remove after

Add will-change via JavaScript before animation, reset with wca after.

Use as last resort

Most CSS transitions/animations auto-optimize. Apply will-change only when performance issues confirmed.