HTML Tutorial 0/139 lessons ~6 min read Lesson 95

    Reflow & Repaint

    reflow & repaint reflow and repaint are main-thread layout and paint invalidations that cause jan reflow (layout) recalculates geometry when

    Course progress0%
    Focus
    18 guided sections
    Practice signal
    Examples included
    Career prep
    Interview Q&A included

    Introduction

    Reflow (layout) recalculates geometry when structure or sizes change. Repaint updates pixels when visual appearance changes without layout. Together they dominate main-thread jank when JavaScript mutates DOM/CSS aggressively. Blink marks invalidation roots; Gecko and WebKit use similar dirty-region strategies.

    Understanding reflow vs repaint vs composite-only updates separates senior frontend engineers from those who only profile network waterfalls.

    Business problem

    Business pressure: Dashboard teams polling server state every second update DOM widths — forced reflow loop pegs main thread, INP exceeds 500ms on CrUX, executives see "frozen" UI during board demos.

    • Conversion: Checkout spinner that triggers layout each frame feels broken — users double-click pay button.
    • Compliance: Dynamic error messages resizing form shift focus — WCAG 3.2.2 violation risk.
    • SEO: Less direct, but main-thread congestion delays render for crawlers simulating slow devices.

    Why this feature exists

    Platform motivation: Engines cannot repaint without knowing geometry (reflow) when layout-affecting properties change. Invalidation system exists to avoid full-page relayout every mutation — but developers can defeat it with read/write thrashing.

    • History: "Reflow" terminology from early IE dev docs; modern specs say "layout"; DevTools still shows both terms.
    • Alternative rejected: Immutable layout tree per frame — memory and CPU prohibitive.
    • Modern role: contain, content-visibility, and ResizeObserver batch layout work.

    Browser internals

    Inside the engine: Layout invalidation bubbles to containing block chain. Paint invalidation uses damage rects — union of changed areas. Sequential layout forced when JS queries geometry mid-mutation. Blink's LayoutNG caches substructures when inputs unchanged.

    • Reflow triggers: width/height, font-size, DOM insert/delete, getComputedStyle on geometric props, window resize.
    • Repaint triggers: color, background, visibility, outline — no geometry change.
    • Composite skip: transform/opacity on layer — neither reflow nor main-thread repaint.
    • DevTools: "Layout" and "Paint" events; "Forced reflow" warning links to triggering JS stack.
    text
    JS: element.style.width = '100px' → layout dirty
    JS: element.offsetWidth → FORCED SYNC LAYOUT
    JS: element.style.background = 'red' → paint dirty (if no layout)
    JS: element.style.transform = '...' → compositor update (if layered)

    Rendering workflow

    Rendering path: Invalidation coalesces per frame via requestAnimationFrame scheduling in browsers. Multiple DOM writes before rAF typically produce one layout pass. Reads before rAF flush force synchronous layout.

    • Critical path: Initial page load layout is full-document; subsequent should be incremental.
    • Layout: O(n) subtree worst case; depth and sibling count matter.
    • Paint: O(area) — large fixed headers repainting hurt scroll if not composited.

    Feature deep dive

    Layout thrashing pattern: for each item, write style then read offsetHeight — N forced layouts. Fix: batch writes, read once. ResizeObserver delivers size after layout batch — preferred over polling offsetWidth in loop.

    • Fast DOM: DocumentFragment, replaceChildren, classList.toggle batch visual state.
    • FLIP technique: First-Last-Invert-Play — measure, mutate, animate with transform (composite).
    • contain: strict: Isolates layout subtree — risky if content overflows unknowingly.
    • Virtual scrolling: Caps DOM size — reduces reflow scope on data updates.
    html
    <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="UTF-8">
    <title>Reflow Demo</title>
    <style>
    #bar { width: 50px; height: 20px; background: #34a853; transition: none; }
    button { margin: 1rem 0.5rem 1rem 0; }
    </style>
    </head>
    <body>
    <h1>Layout Thrashing Demo</h1>
    <button id="bad">Bad: read/write loop</button>
    <button id="good">Good: batch writes</button>
    <div id="bar"></div>
    <script>
    const bar = document.getElementById('bar');
    document.getElementById('bad').onclick = () => {
    for (let i = 0; i < 100; i++) {
    bar.style.width = i + 'px';
    bar.offsetWidth;
    }
    };
    document.getElementById('good').onclick = () => {
    bar.style.width = '100px';
    requestAnimationFrame(() => console.log('width:', bar.offsetWidth));
    };
    </script>
    </body>
    </html>

    Accessibility analysis

    A11y architecture: Reflow from dynamic content changes reading order for zoom users. aria-live polite regions batch announcements — avoid reflowing live region height every 100ms.

    • Screen readers: Layout change may move element under cursor — disorienting during announce.
    • Keyboard: Focused element reflowed off-screen should scrollIntoView on expand.
    • WCAG: Reflow criterion (1.4.10) — content must reflow at 320px without horizontal scroll.

    SEO impact

    SEO architecture: CLS accumulates from reflow shifts. SEO teams monitor layout instability alongside content visibility — hidden content expansion shifts ranking-adjacent UX signals in CrUX.

    • Crawl: Excessive lazy hydration reflow may delay text stability for extraction.
    • Rich results: Stable layout helps visual price consistency with schema.
    • Core Web Vitals: CLS is direct measure of harmful reflow; INP captures post-reflow handler delay.

    Security considerations

    Security boundary: Layout timing attacks largely patched; still avoid leaking sensitive state via scroll measurement gadgets in embedded contexts.

    • XSS: Malicious script can force expensive reflow loops — DoS on client.
    • CSP: Limits script source — reduces injection-driven thrashing.
    • Clickjacking: Reflow moves click target under finger — UI redress variant.

    Performance impact

    Performance: Paul Lewis (Chrome team) "Avoid large, complex layouts and layout thrashing" remains canonical. Target <16ms per frame; layout+painting >10ms leaves little budget for JS.

    • LCP: Late reflow when font swaps changes hero size — reserve metrics.
    • INP: Handler causing reflow before response paint delays INP event timing.
    • CLS: Each unexpected reflow may add to CLS score if elements shift.

    Real production example

    Production pattern: Google Search results use contain and careful DOM updates to limit reflow scope on infinite scroll. FLIP animations for expanding result cards — transform only, documented in web.dev animation guides.

    • Pattern: Read layout in rAF callback; write in previous frame or use CSS classes.
    • Monitoring: Long Animation Frames API reports layout duration to RUM.
    • Fix: Replaced offsetWidth loop with ResizeObserver — INP p75 dropped 120ms.

    Enterprise usage

    Enterprise: Spreadsheet-like UIs isolate cell reflow with virtualized rows/columns. Fixed grid tracks prevent cascade reflow across entire sheet.

    • Design system: Expand/collapse uses max-height transition sparingly — triggers layout; prefer transform scaleY on composited layer or allow-discrete.
    • CMS: Ad slots sized with aspect-ratio CSS — reflow contained to slot.
    • CI gates: Performance tests assert no forced reflow in interaction traces.

    Common production failures

    What breaks in prod: Resize handler calling setState on every pixel of window drag — thousands of reflows per second, browser hang reports.

    • Incident: Tooltip positioned with getBoundingClientRect in mousemove — 60 forced layouts/sec.
    • SEO regression: Accordion without min-height reserve — CLS 0.38 on FAQ landing pages.
    • Perf regression: "Optimize" replaced transform hover with box-shadow transition — full repaints every frame.

    Architecture review questions

    • Do any hot paths interleave geometric reads and style writes?
    • Are window resize handlers debounced and avoiding synchronous layout reads?
    • Do expanding UI patterns reserve space or use compositor-safe animation?
    • What is layout duration p95 in Long Animation Frames RUM data?
    • Are lists virtualized beyond 200 visible rows?
    • Does font loading use size-adjust to minimize reflow on swap?

    Hands-on project

    Project: Implement auto-sizing tooltip two ways: (1) mousemove + getBoundingClientRect each event, (2) cached position updated on rAF. Compare Performance forced reflow count.

    • Deliverable: Code + trace screenshots + INP interaction test in DevTools.
    • Verify: web.dev layout thrashing article checklist.
    • Stretch: Rewrite expand animation with FLIP + transform.

    Interview questions

    Difference between reflow, repaint, and composite?(Advanced)

    Reflow/layout recalculates geometry. Repaint redraws pixels when visuals change without geometry. Composite updates GPU layers for transform/opacity without main-thread layout/paint. Cost generally: reflow ≥ repaint > composite.

    Follow-up: Does opacity always composite-only?

    What is layout thrashing and how do you fix it?(Intermediate)

    Alternating DOM writes and geometric reads force sync layout each iteration. Fix: batch all writes, then read once in requestAnimationFrame; use ResizeObserver; use CSS classes instead of incremental style tweaks in loops.

    Follow-up: Name APIs that force layout.

    How does contain: layout help?(Advanced)

    Tells engine layout changes inside element don't affect outside — limits invalidation subtree. Improves reflow cost for isolated widgets. Must ensure no overflow clipping breaks design.

    Follow-up: layout vs strict vs content?

    Try it yourself

    Edit the HTML, CSS, or JS panels — the preview updates as you type.

    Try it yourself

    Preview

    Summary

    Reflow and repaint are main-thread layout and paint invalidations that cause jank when misused. Chrome team guidance on web.dev emphasizes avoiding layout thrashing and preferring compositor properties — profile with DevTools Forced reflow warnings.

    Ready to mark this lesson complete?Track your journey across the entire course.