Fix Web Rendering Performance
Diagnose and reduce expensive layout, paint, raster, and compositing work in web interfaces, including animations and
static visual effects repeated across scrolling lists or grids.
how to use
/fix-web-rendering-performance Apply these guidelines to web rendering, visual effects, and animation work in this
conversation.
/fix-web-rendering-performance <file> Review the file against the relevant guidance below and report:
- violations (quote the exact line or snippet)
- why it matters (one short sentence)
- a concrete fix (code-level suggestion)
Do not migrate animation libraries unless explicitly requested. Apply rules within the existing stack.
Treat rendering guidance as performance heuristics. A CSS property can identify a risk, but a runtime profile is needed
to establish a bottleneck and verify an improvement.
when to apply
Reference these guidelines when:
- adding or changing UI animations (CSS, WAAPI, Motion, rAF, GSAP)
- refactoring janky interactions or transitions
- implementing scroll-linked motion or reveal-on-scroll
- animating layout, filters, masks, gradients, or CSS variables
- reviewing components that use will-change, transforms, or measurement
- reviewing scrolling lists or grids with repeated filters, shadows, blur, or backdrop effects, including virtualized
lists
rendering steps glossary
- composite candidates: transform and opacity; verify actual layer behavior
- paint/raster: changing colors, borders, gradients, masks, images, or shadows can require redrawing pixels
- filters: filter and backdrop-filter costs depend on the effect, affected pixels, and browser acceleration
- layout: changes to size, position, or flow can require recalculating geometry
rule categories by priority
| priority |
category |
impact |
| 1 |
never patterns |
critical |
| 2 |
choose the mechanism |
critical |
| 3 |
measurement |
high |
| 4 |
scroll |
high |
| 5 |
paint |
medium-high |
| 6 |
layers |
medium |
| 7 |
repeated effects |
medium-high |
| 8 |
view transitions |
low |
| 9 |
tool boundaries |
critical |
quick reference
1. never patterns (critical)
- do not interleave layout reads and writes in the same frame
- do not animate layout continuously on large or meaningful surfaces
- do not drive animation from scrollTop, scrollY, or scroll events
- no requestAnimationFrame loops without a stop condition
- do not mix multiple animation systems that each measure or mutate layout
2. choose the mechanism (critical)
- default to transform and opacity for motion
- use JS-driven animation only when interaction requires it
- paint or layout animation is acceptable only on small, isolated surfaces
- one-shot effects are acceptable more often than continuous motion
- prefer downgrading technique over removing motion entirely
3. measurement (high)
- measure once, then animate via transform or opacity
- batch all DOM reads before writes
- do not read layout repeatedly during an animation
- prefer FLIP-style transitions for layout-like effects
- prefer approaches that batch measurement and writes
- profile realistic scrolling and animation on target browsers and devices; inspect frame times, dropped frames,
paint/raster work, and compositing alongside JavaScript and framework render timings
4. scroll (high)
- prefer Scroll or View Timelines for scroll-linked motion when available
- use IntersectionObserver for visibility and pausing
- do not poll scroll position for animation
- pause or stop animations when off-screen
- scroll-linked motion must not trigger continuous layout or paint on large surfaces
5. paint (medium-high)
- paint-triggering animation is allowed only on small, isolated elements
- do not animate paint-heavy properties on large containers
- do not animate CSS variables for transform, opacity, or position
- do not animate inherited CSS variables
- scope animated CSS variables locally and avoid inheritance
6. layers (medium)
- compositor motion requires layer promotion, never assume it
- use will-change temporarily and surgically
- avoid many or large promoted layers
- avoid applying will-change to every list row; layer promotion does not eliminate an effect's rendering cost
- validate layer behavior with tooling when performance matters
7. blur, shadows, and repeated effects (medium-high)
Blur and blurred shadows can cost more to render than simple fills. The cost varies with the filter, radius, affected
area, browser, and device. See CSS filter performance and
paint profiling guidance.
- assess repeated filter, backdrop-filter, blurred box-shadow, and drop-shadow effects across the rendered list or grid;
many individually small effects can become a substantial aggregate workload
- virtualization normally reduces work by limiting rendered items; it does not remove the cost of effects on those
items. Include visible rows, overscan, and newly entering rows in measurements, and tune overscan to avoid both
excessive work and blank flashes. See
list virtualization guidance
- profile backdrop-filter while content behind it scrolls or animates, even if the filter value is static; the filtered
input changes with the backdrop. See the
Filter Effects draft rendering model and
backdrop-filter guidance
- distinguish animating filter or shadow parameters from moving an item whose static rendering can be reused; evaluate
the rendering pipeline before calling either a bottleneck
- prefer smaller affected areas, fewer shadow layers, and simpler fills or borders when repeated decoration is a
measured bottleneck; preserve meaningful focus and state indicators
- prefer transform and opacity when they convey the intended motion; keep necessary blur animations brief and localized,
and profile continuous or large-area effects explicitly
- do not prescribe a universal safe blur radius such as 8px; choose limits from measurements of the actual workload
8. view transitions (low)
- use view transitions only for navigation-level changes
- avoid view transitions for interaction-heavy UI
- avoid view transitions when interruption or cancellation is required
- treat size changes as potentially layout-triggering
9. tool boundaries (critical)
- do not migrate or rewrite animation libraries unless explicitly requested
- apply these rules within the existing animation system
- never partially migrate APIs or mix styles within the same component
common fixes
/* layout thrashing: animate transform instead of width */
/* before */
.panel {
transition: width 0.3s;
}
/* after */
.panel {
transition: transform 0.3s;
}
/* scroll-linked: use scroll-timeline instead of JS */
/* before */
window.addEventListener('scroll', () => el.style.opacity = scrollY / 500)
/* after */ .reveal {
animation: fade-in linear;
animation-timeline: view();
}
// measurement: batch reads before writes (FLIP)
// before: layout thrash
el.style.left = el.getBoundingClientRect().left + 10 + "px"
// after: measure once, animate via transform
const first = el.getBoundingClientRect()
el.classList.add("moved")
const last = el.getBoundingClientRect()
el.style.transform = `translateX(${first.left - last.left}px)`
requestAnimationFrame(() => {
el.style.transition = "transform 0.3s"
el.style.transform = ""
})
review guidance
- enforce critical rules first (never patterns, tool boundaries)
- choose the least expensive rendering work that matches the intent
- for any non-default choice, state the constraint that justifies it (surface size, duration, or interaction
requirement)
- when reviewing, prefer actionable notes and concrete alternatives over theory
- verify suspected effect costs by comparing the same interaction with effects enabled and simplified or disabled;
report the browser/device, rendered item count including overscan, and observed frame/rendering changes
- label findings from source inspection as potential hotspots until profiling confirms their impact
1---2name: fix-web-rendering-performance3description: Audit and fix web rendering performance, including animations, scrolling, layout thrashing, and expensive visual effects. Use for janky interactions or costly CSS rendering, such as repeated background blur, backdrop filters, and shadows in lists or grids, even when those effects are not animated.4---56# Fix Web Rendering Performance78Diagnose and reduce expensive layout, paint, raster, and compositing work in web interfaces, including animations and9static visual effects repeated across scrolling lists or grids.1011## how to use1213- `/fix-web-rendering-performance` Apply these guidelines to web rendering, visual effects, and animation work in this14 conversation.1516- `/fix-web-rendering-performance <file>` Review the file against the relevant guidance below and report:17 - violations (quote the exact line or snippet)18 - why it matters (one short sentence)19 - a concrete fix (code-level suggestion)2021Do not migrate animation libraries unless explicitly requested. Apply rules within the existing stack.2223Treat rendering guidance as performance heuristics. A CSS property can identify a risk, but a runtime profile is needed24to establish a bottleneck and verify an improvement.2526## when to apply2728Reference these guidelines when:2930- adding or changing UI animations (CSS, WAAPI, Motion, rAF, GSAP)31- refactoring janky interactions or transitions32- implementing scroll-linked motion or reveal-on-scroll33- animating layout, filters, masks, gradients, or CSS variables34- reviewing components that use will-change, transforms, or measurement35- reviewing scrolling lists or grids with repeated filters, shadows, blur, or backdrop effects, including virtualized36 lists3738## rendering steps glossary3940- composite candidates: transform and opacity; verify actual layer behavior41- paint/raster: changing colors, borders, gradients, masks, images, or shadows can require redrawing pixels42- filters: filter and backdrop-filter costs depend on the effect, affected pixels, and browser acceleration43- layout: changes to size, position, or flow can require recalculating geometry4445## rule categories by priority4647| priority | category | impact |48| -------- | -------------------- | ----------- |49| 1 | never patterns | critical |50| 2 | choose the mechanism | critical |51| 3 | measurement | high |52| 4 | scroll | high |53| 5 | paint | medium-high |54| 6 | layers | medium |55| 7 | repeated effects | medium-high |56| 8 | view transitions | low |57| 9 | tool boundaries | critical |5859## quick reference6061### 1. never patterns (critical)6263- do not interleave layout reads and writes in the same frame64- do not animate layout continuously on large or meaningful surfaces65- do not drive animation from scrollTop, scrollY, or scroll events66- no requestAnimationFrame loops without a stop condition67- do not mix multiple animation systems that each measure or mutate layout6869### 2. choose the mechanism (critical)7071- default to transform and opacity for motion72- use JS-driven animation only when interaction requires it73- paint or layout animation is acceptable only on small, isolated surfaces74- one-shot effects are acceptable more often than continuous motion75- prefer downgrading technique over removing motion entirely7677### 3. measurement (high)7879- measure once, then animate via transform or opacity80- batch all DOM reads before writes81- do not read layout repeatedly during an animation82- prefer FLIP-style transitions for layout-like effects83- prefer approaches that batch measurement and writes84- profile realistic scrolling and animation on target browsers and devices; inspect frame times, dropped frames,85 paint/raster work, and compositing alongside JavaScript and framework render timings8687### 4. scroll (high)8889- prefer Scroll or View Timelines for scroll-linked motion when available90- use IntersectionObserver for visibility and pausing91- do not poll scroll position for animation92- pause or stop animations when off-screen93- scroll-linked motion must not trigger continuous layout or paint on large surfaces9495### 5. paint (medium-high)9697- paint-triggering animation is allowed only on small, isolated elements98- do not animate paint-heavy properties on large containers99- do not animate CSS variables for transform, opacity, or position100- do not animate inherited CSS variables101- scope animated CSS variables locally and avoid inheritance102103### 6. layers (medium)104105- compositor motion requires layer promotion, never assume it106- use will-change temporarily and surgically107- avoid many or large promoted layers108- avoid applying will-change to every list row; layer promotion does not eliminate an effect's rendering cost109- validate layer behavior with tooling when performance matters110111### 7. blur, shadows, and repeated effects (medium-high)112113Blur and blurred shadows can cost more to render than simple fills. The cost varies with the filter, radius, affected114area, browser, and device. See [CSS filter performance](https://web.dev/articles/understanding-css) and115[paint profiling guidance](https://web.dev/articles/animations-guide).116117- assess repeated filter, backdrop-filter, blurred box-shadow, and drop-shadow effects across the rendered list or grid;118 many individually small effects can become a substantial aggregate workload119- virtualization normally reduces work by limiting rendered items; it does not remove the cost of effects on those120 items. Include visible rows, overscan, and newly entering rows in measurements, and tune overscan to avoid both121 excessive work and blank flashes. See122 [list virtualization guidance](https://web.dev/articles/virtualize-long-lists-react-window#overscanning)123- profile backdrop-filter while content behind it scrolls or animates, even if the filter value is static; the filtered124 input changes with the backdrop. See the125 [Filter Effects draft rendering model](https://drafts.csswg.org/filter-effects-2/) and126 [backdrop-filter guidance](https://web.dev/articles/backdrop-filter#basics)127- distinguish animating filter or shadow parameters from moving an item whose static rendering can be reused; evaluate128 the [rendering pipeline](https://web.dev/articles/animations-overview) before calling either a bottleneck129- prefer smaller affected areas, fewer shadow layers, and simpler fills or borders when repeated decoration is a130 measured bottleneck; preserve meaningful focus and state indicators131- prefer transform and opacity when they convey the intended motion; keep necessary blur animations brief and localized,132 and profile continuous or large-area effects explicitly133- do not prescribe a universal safe blur radius such as 8px; choose limits from measurements of the actual workload134135### 8. view transitions (low)136137- use view transitions only for navigation-level changes138- avoid view transitions for interaction-heavy UI139- avoid view transitions when interruption or cancellation is required140- treat size changes as potentially layout-triggering141142### 9. tool boundaries (critical)143144- do not migrate or rewrite animation libraries unless explicitly requested145- apply these rules within the existing animation system146- never partially migrate APIs or mix styles within the same component147148## common fixes149150```css151/* layout thrashing: animate transform instead of width */152/* before */153.panel {154 transition: width 0.3s;155}156/* after */157.panel {158 transition: transform 0.3s;159}160161/* scroll-linked: use scroll-timeline instead of JS */162/* before */163window.addEventListener('scroll', () => el.style.opacity = scrollY / 500)164/* after */ .reveal {165 animation: fade-in linear;166 animation-timeline: view();167}168```169170```js171// measurement: batch reads before writes (FLIP)172// before: layout thrash173el.style.left = el.getBoundingClientRect().left + 10 + "px"174// after: measure once, animate via transform175const first = el.getBoundingClientRect()176el.classList.add("moved")177const last = el.getBoundingClientRect()178el.style.transform = `translateX(${first.left - last.left}px)`179requestAnimationFrame(() => {180 el.style.transition = "transform 0.3s"181 el.style.transform = ""182})183```184185## review guidance186187- enforce critical rules first (never patterns, tool boundaries)188- choose the least expensive rendering work that matches the intent189- for any non-default choice, state the constraint that justifies it (surface size, duration, or interaction190 requirement)191- when reviewing, prefer actionable notes and concrete alternatives over theory192- verify suspected effect costs by comparing the same interaction with effects enabled and simplified or disabled;193 report the browser/device, rendered item count including overscan, and observed frame/rendering changes194- label findings from source inspection as potential hotspots until profiling confirms their impact