Rendering and the Event Loop in JavaScript
The browser renders between macrotasks. Microtasks can delay the paint. Here is how it works.
Rendering and the Event Loop in JavaScript
The browser renders (paints) between macrotasks. Microtasks run before rendering. Understanding this helps you build smooth UIs.
The Event Loop With Rendering
- Run synchronous code (and the current macrotask).
- Drain all microtasks.
- Render (if needed usually ~60fps, every 16.6ms).
- Take the next macrotask.
- Repeat from step 2.
What This Means
- Microtasks run before rendering: if a microtask changes the DOM, the browser paints the change in the same frame.
- Macrotasks run after rendering: a macrotask's DOM changes are painted in the next frame.
- Long microtasks delay rendering: if microtasks take 100ms, the paint is delayed by 100ms (frame drop).
Example: Microtask Before Paint
element.style.color = "red"; Promise.resolve().then(() => { element.style.color = "blue"; }); // the user sees "blue" (microtask runs before paint) `` The microtask changes the color to blue before the browser paints. The user never sees red. ### Example: Macrotask After Paint ```js element.style.color = "red"; setTimeout(() => { element.style.color = "blue"; }, 0); // the user sees "red" then "blue" (macrotask runs after paint) `` The macrotask runs after the browser paints red. The user sees red, then blue in the next frame. ### `requestAnimationFrame` ```js function animate() { element.style.left = parseInt(element.style.left) + 1 + "px"; requestAnimationFrame(animate); } requestAnimationFrame(animate); `` `requestAnimationFrame` schedules a callback **before the next paint**. It is the correct way to do visual animations. It syncs with the browser's refresh rate (usually 60fps). ### Long Tasks and Frame Drops If a synchronous task (or a batch of microtasks) takes more than 16.6ms, the browser drops a frame. The animation looks janky. **Fix**: Break long tasks into chunks with `setTimeout` or use `requestIdleCallback` for low-priority work. ### `requestIdleCallback` ```js requestIdleCallback((deadline) => { while (deadline.timeRemaining() > 0 && hasWork()) { doWork(); } }); `` Runs work when the browser is idle (between frames). The `deadline` tells you how much time is left. Useful for non-critical work that should not cause frame drops. ### The Takeaway The browser renders between macrotasks. Microtasks run before rendering (DOM changes in microtasks are painted in the same frame). Long microtasks or macrotasks cause frame drops. Use `requestAnimationFrame` for animations and `requestIdleCallback` for low-priority work. Keep tasks under 16.6ms for smooth 60fps.
Between macrotasks. The event loop runs a macrotask, drains all microtasks, then renders (if needed, usually at 60fps). Then takes the next macrotask. Microtasks run before rendering.
Yes. The event loop drains all microtasks before rendering. If microtasks take longer than 16.6ms, the paint is delayed and a frame is dropped. Keep microtasks short for smooth rendering.
A callback that runs before the next paint. It syncs with the browser's refresh rate (usually 60fps). It is the correct way to do visual animations. Use it instead of setTimeout for animations.
A callback that runs when the browser is idle (between frames). The deadline object tells you how much time is left. Useful for non-critical work that should not cause frame drops.
Because microtasks run before rendering. If you set color to red and a microtask sets it to blue, the browser paints blue (microtask runs before paint). The user never sees red. With setTimeout (macrotask), the user sees red then blue.
Ready to master React completely?
Want to upskill yourself, crack your next interview, and get your dream job? Join our comprehensive course to dive deeper with high-quality video tutorials, solve interview questions, and a premium community.
Master React
Want to upskill yourself, crack your next interview, and get your dream job? Join our comprehensive course.

