How can you optimize DOM manipulation for better performance?
TL;DR
Measure with browser performance tools, then reduce unnecessary DOM work, batch related mutations, keep DOM trees manageable, and avoid forced synchronous layout by grouping reads before writes. Build detached subtrees before inserting them, and use requestAnimationFrame() for visual updates. A framework or virtual DOM changes the programming model and may batch work, but it is not an automatic performance optimization.
How can you optimize DOM manipulation for better performance?
Minimize direct DOM access
DOM access is not inherently a problem, but repeated queries and mutations can become costly when they trigger style, layout, paint, or large tree updates. Reuse stable references where that improves clarity and avoids repeated work.
// Creating an element for the example belowconst newElement = document.createElement('div');newElement.id = 'myElement';document.body.appendChild(newElement);const element = document.getElementById('myElement'); // Access once// Then use the reference to edit styleelement.style.color = 'red';element.style.backgroundColor = 'blue';console.log(document.body); // Notice the edits
Batch DOM changes
Instead of making multiple changes to the DOM one at a time, batch them together. This reduces the number of reflows and repaints.
// Creating an element for the example belowconst newElement = document.createElement('div');newElement.id = 'myElement';document.body.appendChild(newElement);const element = document.getElementById('myElement');element.style.cssText = 'color: red; background-color: blue;';console.log(document.body); // Notice the edits
Use DocumentFragment
When adding multiple elements to the DOM, use a DocumentFragment to minimize reflows and repaints.
const fragment = document.createDocumentFragment();for (let i = 0; i < 100; i++) {const newElement = document.createElement('div');newElement.textContent = `Item ${i}`;fragment.appendChild(newElement);}document.body.appendChild(fragment);console.log(document.body); // Notice that the divs have been added
Frameworks and virtual DOMs
Libraries such as React provide declarative rendering and can batch updates. Their reconciliation also adds JavaScript and memory overhead, so use a framework for application architecture and measure hot paths rather than assuming its virtual representation is faster than focused DOM code.
import React from 'react';import { createRoot } from 'react-dom/client';const App = () => (<div><h1>Hello, world!</h1></div>);const root = createRoot(document.getElementById('root'));root.render(<App />);
Use requestAnimationFrame for animations
For smoother animations, use requestAnimationFrame to ensure updates are synchronized with the browser's repaint cycle.
function animate() {// Update animation staterequestAnimationFrame(animate);}requestAnimationFrame(animate);
Avoid layout thrashing
Layout thrashing occurs when you read and write to the DOM repeatedly in a way that forces the browser to recalculate styles and layout multiple times. To avoid this, separate read and write operations.
// Creating an element for the example belowconst newElement = document.createElement('div');newElement.id = 'myElement';document.body.appendChild(newElement);const element = document.getElementById('myElement');const height = element.clientHeight; // Readelement.style.height = `${height + 10}px`; // Write
Further reading
- FreeCodeCamp: DOM Manipulation in JavaScript
- Google Developers: Avoid large, complex layouts and layout thrashing
- React documentation
- MDN Web Docs: Using requestAnimationFrame