Reflow vs Repaint
Reflow, more commonly called layout, recalculates the size and position of elements. Repaint redraws visual pixels after styles change without necessarily recalculating geometry.
Geometry changes: Style -> Layout/Reflow -> Paint -> Composite
Visual changes: Style -----------------> Paint -> Composite
Layer changes: Style -------------------------> Composite
One-line idea: Reflow answers "where and how large?" while repaint answers "what should it look like?"
Core Concepts
Where They Fit in Browser Rendering
After the browser parses HTML and CSS, visual updates can move through several stages:
JavaScript or CSS change
↓
Style calculation
↓
Layout (reflow)
↓
Paint and rasterization
↓
Compositing
Not every update requires every stage. The browser starts from the earliest stage invalidated by the change and performs the later work needed to display the result.
What Is Reflow?
During layout, the browser calculates the geometry of rendered boxes:
- width and height
- position
- margins and padding
- text wrapping
- relationships between parents, children and siblings
Reflow generally means performing some of that layout work again after the page changes. Developer tools and browser documentation may use the terms layout and reflow for the same underlying kind of work.
panel.style.width = "500px";
Changing the panel's width can affect its children, nearby elements and text wrapping. The browser may need to recalculate more than the changed element itself.
Common Reflow Triggers
Changes that can affect geometry include:
- adding or removing DOM elements
- changing
width,height,padding,marginor borders - changing layout modes such as
display, grid or flex properties - changing content that affects an element's size
- loading a font or image without reserved dimensions
- resizing the viewport or changing device orientation
- reading geometry while style changes are pending
list.appendChild(newItem);
card.style.padding = "24px";
heading.textContent = "A much longer heading that wraps";
A property does not have one universal cost in every situation. The affected layout area depends on document structure, containment and browser optimization.
What Is Repaint?
During paint, the browser records and rasterizes visual details such as text, colors, borders, backgrounds and shadows.
A repaint happens when pixels need to be redrawn, even when element geometry stays the same.
button.style.color = "white";
button.style.backgroundColor = "royalblue";
These changes usually affect appearance without changing the button's size or position, so layout may be skipped.
Common Repaint Triggers
Paint-only changes commonly include:
colorbackground-colororbackground-imageborder-colorbox-shadowoutline- visual content changes that preserve geometry
The browser tries to repaint only invalidated regions rather than the entire page. Large elements, complex effects and frequent updates can still make paint expensive.
Reflow vs Repaint
| Area | Reflow or Layout | Repaint |
|---|---|---|
| Main question | Where is the element and how large is it? | How should the element's pixels look? |
| Recalculates geometry | Yes | No |
| Typical trigger | Size, position or document-flow change | Color, background or shadow change |
| Later work | Usually paint and composite | Usually composite |
| Possible scope | Element, subtree or wider document | Invalidated visual region |
| Common risk | Repeated or forced layout | Large or complex paint areas |
Reflow is often more expensive because geometry dependencies can spread through the document and the result usually needs painting afterward. This is a useful tendency, not a rule that every repaint is cheap or every layout is slow.
Reflow, Repaint and Compositing
A Reflow Usually Leads to a Repaint
When geometry changes, the browser normally needs to redraw the affected content:
Change width
↓
Recalculate layout
↓
Repaint affected pixels
↓
Composite the result
The reverse is not always true. A color change can require repainting without recalculating layout.
Composite-Only Updates
The browser may paint parts of a page into separate layers and then combine those layers during compositing. Some transform and opacity animations can update layer properties without repeating layout or paint on each frame.
.menu {
transform: translateX(-100%);
transition: transform 200ms ease;
}
.menu.is-open {
transform: translateX(0);
}
This is commonly smoother than animating a geometry property such as left:
/* More likely to require layout and paint on each frame */
.menu {
left: -100%;
transition: left 200ms ease;
}
Compositing is not free. Layers use memory, and browsers decide how to create and manage them. Avoid adding will-change broadly or permanently just to force layer promotion.
Layout Is Not the Same as Layout Shift
A page can perform layout without visibly moving existing content. A layout shift is an unexpected visible movement measured for visual stability, while layout is the browser's geometry calculation.
Layout work may occur without a visible shift.
A visible shift normally requires updated layout and rendering.
Reserving image dimensions reduces unexpected shifts and can also avoid extra layout when the image arrives:
<img src="profile.jpg" width="320" height="240" alt="User profile" />
Forced Reflow and Layout Thrashing
Normal Asynchronous Layout
Browsers normally batch style and layout changes until they need to produce a frame. Several writes can therefore be handled together:
card.classList.add("expanded");
card.setAttribute("aria-expanded", "true");
card.textContent = "Expanded content";
Forced Synchronous Layout
Some JavaScript properties and methods need current geometry. If styles are pending, reading them can force the browser to run layout immediately.
box.style.width = "400px"; // Write: layout is now invalid
const width = box.offsetWidth; // Read: browser may calculate layout now
Common geometry reads include:
offsetWidthandoffsetHeightclientWidthandclientHeightscrollWidthandscrollHeightgetBoundingClientRect()- some computed-style values
These APIs are not bad by themselves. The problem is requesting current geometry repeatedly after invalidating it.
Layout Thrashing
Alternating a style write and geometry read inside a loop can force repeated layouts:
// Problematic: write, read, write, read...
for (const item of items) {
item.style.width = `${container.offsetWidth / 2}px`;
}
Read first, then perform the writes:
const itemWidth = container.offsetWidth / 2;
for (const item of items) {
item.style.width = `${itemWidth}px`;
}
Thrashing: Write -> Layout -> Write -> Layout -> Write -> Layout
Batching: Read once -> Write -> Write -> Write -> One later layout
Practical Optimization Patterns
Batch DOM Reads and Writes
Collect required measurements before changing styles. Then apply related updates together, often by changing a class:
const currentHeight = panel.getBoundingClientRect().height;
requestAnimationFrame(() => {
panel.classList.add("expanded");
panel.style.setProperty("--previous-height", `${currentHeight}px`);
});
requestAnimationFrame() schedules work near the next frame, but it does not make expensive layout or JavaScript free. The callback still needs to finish quickly.
Prefer Appropriate Animation Properties
For movement and fading, prefer transform and opacity when they provide the required design:
.toast {
opacity: 0;
transform: translateY(12px);
transition: opacity 160ms, transform 160ms;
}
.toast.is-visible {
opacity: 1;
transform: translateY(0);
}
Do not replace every layout animation blindly. Correct behavior, accessibility and visual quality still matter, and the final result should be measured.
Reduce the Scope of Work
- Keep the DOM no larger or deeper than the interface requires.
- Virtualize long lists when only a small portion is visible.
- Reserve space for images, ads and asynchronously loaded content.
- Use CSS containment where a genuine component boundary allows it.
- Avoid changing high-level containers when only a small child needs updating.
- Remove paint-heavy effects when profiling shows they are costly.
Measure with Browser Tools
Use the browser Performance panel to record the exact slow page load, animation or interaction. Inspect:
- Recalculate Style and Layout events
- forced-reflow warnings and their JavaScript call sites
- Paint and raster activity
- dropped or long frames
- unexpectedly large paint regions
- layout-shift events
Chrome's Rendering panel can enable Paint flashing to highlight areas being repainted. Treat this as diagnostic evidence, not a performance score by itself.
Common Mistakes
| Mistake | Better approach |
|---|---|
| Saying every DOM change causes a full-page reflow | Explain that browsers invalidate only the required work when possible |
| Treating reflow and repaint as independent events | Reflow usually causes later paint, while repaint can occur without reflow |
| Assuming repaint is always cheap | Consider the painted area, visual effect and update frequency |
| Avoiding all geometry reads | Avoid repeated reads after writes; measure when geometry is genuinely needed |
| Calling any layout calculation a CLS problem | Separate rendering work from unexpected visible layout shifts |
Animating top, left, width or height by default | Prefer compositor-friendly properties when the design allows |
Adding will-change to many elements | Use it sparingly and remove it when no longer needed |
Assuming requestAnimationFrame() fixes slow code | Keep the scheduled work within the frame budget |
| Optimizing from a list of CSS properties alone | Record the real interaction and follow the measured bottleneck |
Official References
- MDN: How Browsers Work
- MDN: Critical Rendering Path
- web.dev: Avoid Large, Complex Layouts and Layout Thrashing
- Chrome DevTools: Analyze Runtime Performance
- Chrome DevTools: Discover Rendering Performance Issues
Summary
Reflow recalculates page geometry, while repaint redraws visual pixels; efficient interfaces avoid unnecessary work by batching updates, choosing suitable animation properties and measuring real browser behavior.
Something changes
↓
Did geometry change?
Yes -> Layout/Reflow -> Paint -> Composite
No
↓
Did pixels change?
Yes -> Paint -> Composite
No -> Composite-only update may be possible