Skip to content
GeeksSmith
advanced 8 min read

Browser Rendering Internals: Reflow, Repaint & GPU Compositing

The browser rendering pipeline turns HTML/CSS into pixels through DOM/CSSOM creation, Layout (Reflow), Paint (rasterization), and Compositing (GPU layers). Optimizing animations requires targeting the Composite-only stage using `transform` and `opacity` to avoid blocking the main JavaScript thread.

Why it matters in interviews

A staple interview question across Tekion Corp, Walmart, Amazon, and Google. Interviewers test your understanding of why layout thrashing occurs (forced synchronous layouts), why CSS `transform` is GPU accelerated, and how to maintain 60/120 FPS during heavy user interactions.

Visual & Interactive Explanation

Interactive Flow Execution

Code Examples & Implementation

Layout Thrashing (Anti-Pattern) vs Batching (Fast)
// ❌ ANTI-PATTERN: Forced Synchronous Layout (Layout Thrashing)
// Interleaving writes and reads in a loop forces Reflow on EVERY iteration
function badResize(elements) {
  for (let i = 0; i < elements.length; i++) {
    // WRITE
    elements[i].style.width = "200px";
    // READ: Forces browser to synchronously recalculate layout right now!
    const height = elements[i].offsetHeight;
    console.log(height);
  }
}

// ✅ FAST PATTERN: Read-all, then Write-all (Batched)
function goodResize(elements) {
  // Phase 1: Batch all reads first
  const heights = elements.map((el) => el.offsetHeight);

  // Phase 2: Batch all writes in requestAnimationFrame
  requestAnimationFrame(() => {
    elements.forEach((el) => {
      el.style.width = "200px";
    });
  });
}
CSS Animation Performance: Top/Left vs Transform
/* ❌ SLOW: Triggers Reflow + Repaint + Composite on Main Thread */
.modal-bad {
  position: absolute;
  top: 0;
  left: 0;
  transition: top 0.3s ease, left 0.3s ease; /* Causes 15-30 FPS jank */
}

/* ✅ FAST: Runs 100% on GPU Compositor Thread (Skips Reflow & Repaint) */
.modal-good {
  position: absolute;
  transform: translate3d(0, 0, 0);
  transition: transform 0.3s cubic-bezier(0.16, 1, 0.3, 1);
  will-change: transform; /* Promotes element to its own GPU RenderLayer */
}

Interview-Ready Answers

Reflow recalculates geometry and bounding boxes; Repaint redraws pixels; Compositing groups layers on the GPU. Animating with `transform` and `opacity` skips both Reflow and Repaint, running smoothly on the GPU Compositor Thread.

Follow-up Questions & Deep Dives

🎯 Code Output — What Does This Print?

Question 1medium
// What triggers Reflow vs Composite?
const el = document.getElementById("box");

// Operation A:
el.style.transform = "translateX(50px)";

// Operation B:
el.style.marginLeft = "50px";

// Which operation triggers a full Layout/Reflow pass?

Common Mistakes & Anti-Patterns

  • Animating CSS `top` or `left` instead of `transform: translate3d()`
  • Reading layout properties inside a loop after mutating DOM styles (Layout Thrashing)
  • Forgetting `{ passive: true }` on touch and wheel event listeners
  • Overusing `will-change` on static elements, consuming excess GPU VRAM

Implementation Evolution: Anti-Pattern to Production

❌ Anti-Pattern: Layout Thrashing inside Scroll Event Listener

// ❌ Anti-Pattern: Reading scroll and mutating DOM inline during high-frequency scroll
window.addEventListener("scroll", () => {
  const scrollTop = window.scrollY;
  const header = document.getElementById("header");

  if (scrollTop > 100) {
    header.style.height = "60px"; // WRITE
  }
  const boxTop = document.getElementById("box").offsetTop; // READ -> FORCED REFLOW
});
Why this breaks: Scroll events fire up to 120 times per second. Reading geometry properties (`offsetTop`) immediately after writing styles causes layout thrashing, dropping frame rates from 60fps to under 20fps.

✅ Correct Pattern: requestAnimationFrame Throttling & Class Toggles

// ✅ Correct: Throttle scroll updates with requestAnimationFrame and toggle CSS classes
let ticking = false;

window.addEventListener("scroll", () => {
  if (!ticking) {
    window.requestAnimationFrame(() => {
      const isScrolled = window.scrollY > 100;
      document.body.classList.toggle("scrolled-nav", isScrolled);
      ticking = false;
    });
    ticking = true;
  }
}, { passive: true });
🚀

🚀 Production-Grade (Hardened): FastDOM Read/Write Batcher Utility

// 🚀 Production: Micro-task scheduled DOM batching engine
class DOMBatcher {
  private reads: Array<() => void> = [];
  private writes: Array<() => void> = [];
  private scheduled = false;

  read(fn: () => void) {
    this.reads.push(fn);
    this.schedule();
  }

  write(fn: () => void) {
    this.writes.push(fn);
    this.schedule();
  }

  private schedule() {
    if (this.scheduled) return;
    this.scheduled = true;

    requestAnimationFrame(() => {
      // Execute all reads first
      const currentReads = this.reads.splice(0);
      currentReads.forEach((fn) => fn());

      // Execute all writes after reads are complete
      const currentWrites = this.writes.splice(0);
      currentWrites.forEach((fn) => fn());

      this.scheduled = false;
    });
  }
}

export const fastdom = new DOMBatcher();

⚠️ Trick Questions & Interviewer Traps

Q1:Which properties trigger Reflow vs Repaint vs Composite?

Q2:Why should you never apply `will-change: transform` to every element on a page?

📋 Rapid Revision Cheat Sheet

  • Reflow (Layout) = Geometry changes (Expensive, walks DOM tree).
  • Repaint = Visual paint changes without geometry (Medium cost).
  • Composite = Layer translation on GPU (Fastest, zero main-thread work).
  • Layout Thrashing = Interleaving DOM reads (`offsetHeight`, `scrollTop`) and writes in JS.
  • Compositor Properties: Stick to `transform` and `opacity` for smooth 60/120 FPS.
  • Hardware Acceleration: Use `translate3d(x,y,0)` or `will-change: transform` judiciously.

Real-World Architectural Scenario

A user opens a sidebar navigation drawer. The slide-in animation drops frames and stutters at 22 FPS on low-end Android devices. Heap snapshots show no JS memory leaks. What is the root cause and fix?

Rate Your Readiness

Rate how comfortably you can explain this in an interview to update your global readiness gauge:

Rate your confidence: