Batch DOM reads and writes
Don't read layout between style writes. Batch them or toggle a class.
Paul Irishgist.github.com/paulirishSee this gist and CSS Triggers for more information on layout-forcing operations.
CSS Triggerscsstriggers.com
See this gist and CSS Triggers for more information on layout-forcing operations.
From vercel-react-best-practices, rule js-batch-dom-css.
One layout instead of hundreds
Resize, then double the boxes and resize again.
Write, read, repeatBad
– ms– forced layouts
el.style.width = w; el.offsetWidth per boxWrites, then readsGood
– ms– forced layouts
writes; reads; writes// 1. every write
boxes.forEach((el, i) => { el.style.width = widths[i] + 'px' })
// 2. every read: the first one lays out, the rest are cached
const rendered = boxes.map((el) => el.offsetWidth)
// 3. every write that depends on a read
boxes.forEach((el, i) => el.classList.toggle('is-wide', rendered[i] >= 20))
Which read forces layout?
Click a step to cycle its type, or pick a preset.
Loop body × 300 boxesSequence
– forced layouts– ms
// Batch writes, then read once
el.style.width = '100px'
el.style.height = '200px'
const { width, height } = el.getBoundingClientRect()
// Or batch reads, then writes
const rect = el.getBoundingClientRect()
el.style.width = rect.width * 2 + 'px'
el.style.height = rect.height * 2 + 'px'
Four writes or one class
Toggle the highlight, then time 500 toggles.
Inline stylesBad
0 style writes0 forced layouts– ms / 500
style=(none)
el.style.width = …; el.offsetWidth; el.style.height = …One class toggleGood
0 class changes0 forced layouts– ms / 500
class=lt-target
el.classList.toggle('highlighted-box').highlighted-box {
width: 100px;
height: 200px;
background-color: blue;
border: 1px solid black;
}
// React: state picks the class, no effect, no ref
function Box({ isHighlighted }) {
return <div className={isHighlighted ? 'highlighted-box' : ''}>Content</div>
}
Why it matters, pros and cons
Why it matters
A read after a write forces layout now. In a loop, that's once per element.
Pros
- One layout instead of one per element.
- Classes cache, theme, and undo in one call.
- React: derive
className, skip the effect.
Cons
- Reads like
datasetortextContentare already free. - One measure per interaction costs a layout already coming.
- Runtime values (drag offset) need a CSS variable, not a class.