Performance & Rendering
Understand the browser's render pipeline. Learn how to avoid Layout Thrashing and keep your application running at a buttery-smooth 60 frames per second.
The 16.6ms Deadline
To achieve a smooth 60 frames per second (fps), the browser must complete all JavaScript execution, style calculations, layout geometry, and painting within 16.6 milliseconds.
If your JavaScript takes 30ms to execute, the browser misses a frame. This is called 'jank'.
The Cost of Layout Thrashing
When you change the DOM (like altering a width or adding a class), the browser invalidates the layout. If you immediately ask the browser for a geometric property (like element.offsetHeight), the browser is forced to pause JavaScript and synchronously recalculate the entire page layout right then and there.
If you do this in a for loop, you trigger **Layout Thrashing**-forcing the browser to recalculate the layout dozens of times in a single frame, destroying performance.
Batching Reads and Writes
The fix is simple: separate your Reads from your Writes.
**Bad (Thrashing):**
javascript
for (let i = 0; i < items.length; i++) {
const h = items[i].offsetHeight; // Read
items[i].style.height = (h + 10) + 'px'; // Write (Invalidates layout!)
}
**Good (Batched):**
javascript
// Read everything first
const heights = items.map(item => item.offsetHeight);
// Then write everything
items.forEach((item, i) => {
item.style.height = (heights[i] + 10) + 'px';
});
The 16.6ms Timeline
Examine the performance profiles below. Notice how mixing Reads and Writes forces the browser into a vicious cycle of expensive layout recalculations, missing the frame deadline. Batching solves this instantly.
The Render Pipeline
// Read forces sync Layout
let h = box.offsetHeight;
// Write invalidates Layout
box.style.height = (h + 10) + 'px';
}
let heights = boxes.map(b => b.offsetHeight);
// 2. Do all Writes last
boxes.forEach((b, i) => {
b.style.height = (heights[i] + 10) + 'px';
});
Check yourself
Pick an answer to lock it in, then read why. Getting one wrong is part of how it sticks.
Remember this
- The browser strives for 60 frames per second. You have **16.6ms** to complete all JavaScript and rendering work per frame.
- If you exceed 16.6ms, the browser drops a frame, resulting in noticeable 'jank'.
- **Layout Thrashing** happens when you interleave DOM reads (like
offsetHeight) and DOM writes (likestyle.height). - Prevent thrashing by **batching**: do all your DOM reads first, save the values, and then do all your DOM writes.
- Always use
requestAnimationFramefor JavaScript-driven animations instead ofsetTimeout.
Done with this concept?
Mark it complete to track your progress. No login needed.