--- title: Avoid Layout Thrashing impact: MEDIUM impactDescription: prevents forced synchronous layouts and reduces performance bottlenecks tags: javascript, dom, css, performance, reflow, layout-thrashing --- ## Avoid Layout Thrashing Avoid interleaving style writes with layout reads. When you read a layout property (like `offsetWidth`, `getBoundingClientRect()`, or `getComputedStyle()`) between style changes, the browser is forced to trigger a synchronous reflow. **This is OK (browser batches style changes):** ```typescript function updateElementStyles(element: HTMLElement) { // Each line invalidates style, but browser batches the recalculation element.style.width = '100px' element.style.height = '200px' element.style.backgroundColor = 'blue' element.style.border = '1px solid black' } ``` **Incorrect (interleaved reads and writes force reflows):** ```typescript function layoutThrashing(element: HTMLElement) { element.style.width = '100px' const width = element.offsetWidth // Forces reflow element.style.height = '200px' const height = element.offsetHeight // Forces another reflow } ``` **Correct (batch writes, then read once):** ```typescript function updateElementStyles(element: HTMLElement) { // Batch all writes together element.style.width = '100px' element.style.height = '200px' element.style.backgroundColor = 'blue' element.style.border = '1px solid black' // Read after all writes are done (single reflow) const { width, height } = element.getBoundingClientRect() } ``` **Correct (batch reads, then writes):** ```typescript function avoidThrashing(element: HTMLElement) { // Read phase - all layout queries first const rect1 = element.getBoundingClientRect() const offsetWidth = element.offsetWidth const offsetHeight = element.offsetHeight // Write phase - all style changes after element.style.width = '100px' element.style.height = '200px' } ``` **Better: use CSS classes** ```css .highlighted-box { width: 100px; height: 200px; background-color: blue; border: 1px solid black; } ``` ```typescript function updateElementStyles(element: HTMLElement) { element.classList.add('highlighted-box') const { width, height } = element.getBoundingClientRect() } ``` **React example:** ```tsx // Incorrect: interleaving style changes with layout queries function Box({ isHighlighted }: { isHighlighted: boolean }) { const ref = useRef(null) useEffect(() => { if (ref.current && isHighlighted) { ref.current.style.width = '100px' const width = ref.current.offsetWidth // Forces layout ref.current.style.height = '200px' } }, [isHighlighted]) return
Content
} // Correct: toggle class function Box({ isHighlighted }: { isHighlighted: boolean }) { return (
Content
) } ``` Prefer CSS classes over inline styles when possible. CSS files are cached by the browser, and classes provide better separation of concerns and are easier to maintain. See [this gist](https://gist.github.com/paulirish/5d52fb081b3570c81e3a) and [CSS Triggers](https://csstriggers.com/) for more information on layout-forcing operations.