What are some common performance bottlenecks in JavaScript applications?
TL;DR
Common bottlenecks include too much JavaScript during startup, long main-thread tasks, repeated layout work, excessive rendering, slow or duplicated network requests, and memory retained by long-lived references. Do not optimize from a checklist: reproduce the slow user action, record it with browser or Node.js profiling tools, fix the dominant cost, and measure again under the same conditions.
Inefficient DOM manipulation
Frequent DOM updates
Frequent DOM updates can be costly because the browser has to re-render the page each time the DOM changes. Batch DOM updates together to minimize reflows and repaints.
// Inefficientfor (let i = 0; i < 1000; i++) {const div = document.createElement('div');div.textContent = i;document.body.appendChild(div);}// Efficientconst fragment = document.createDocumentFragment();for (let i = 0; i < 1000; i++) {const div = document.createElement('div');div.textContent = i;fragment.appendChild(div);}document.body.appendChild(fragment);
Layout thrashing
Layout thrashing occurs when you read and write to the DOM repeatedly, causing multiple reflows and repaints. Minimize layout thrashing by batching reads and writes separately.
// Inefficientboxes.forEach((box) => {const height = box.offsetHeight; // Readbox.style.height = `${height + 10}px`; // Write});// Efficient// Batch readconst heights = [];boxes.forEach((box) => {heights.push(box.offsetHeight);});// Batch writeboxes.forEach((box, i) => {box.style.height = `${heights[i] + 10}px`;});
Blocking the main thread
Heavy computations
Heavy computations can block the main thread, making the UI unresponsive. Use web workers to offload heavy computations to a background thread.
// Main threadconst worker = new Worker('worker.js');worker.postMessage('start');// worker.jsself.onmessage = function (e) {if (e.data === 'start') {// Perform heavy computationself.postMessage('done');}};
Moving work to a worker is worthwhile only when the computation is large enough to justify startup and message-transfer costs. Smaller work may be better split into chunks so the browser can process input and render between them.
Memory leaks
Memory leaks occur when reachable references retain data the application no longer needs. Circular references alone are not leaks in modern tracing garbage collectors. Common causes include unbounded caches, forgotten timers, detached DOM nodes, observers, and listeners attached to long-lived targets.
Unremoved event listeners
const controller = new AbortController();window.addEventListener('resize', handleResize, {signal: controller.signal,});// When this feature is disposed:controller.abort();
Improper use of asynchronous operations
Unoptimized promises
Promise chains and async/await are equivalent ways to express the same asynchronous control flow. Performance suffers when independent operations are awaited one after another; start them together when their results do not depend on each other.
// Inefficient: independent requests run sequentially.async function fetchDashboardDataSequentially() {const user = await fetch('/api/user').then((response) => response.json());const notifications = await fetch('/api/notifications').then((response) =>response.json(),);return { user, notifications };}// Efficient: independent requests run concurrently.async function fetchDashboardDataConcurrently() {const [user, notifications] = await Promise.all([fetch('/api/user').then((response) => response.json()),fetch('/api/notifications').then((response) => response.json()),]);return { user, notifications };}
Debouncing and throttling
Use debouncing and throttling to limit the rate of function execution, especially for event handlers.
// Debouncingfunction debounce(func, wait) {let timeout;return function (...args) {clearTimeout(timeout);timeout = setTimeout(() => func.apply(this, args), wait);};}// Throttlingfunction throttle(func, limit) {let inThrottle;return function (...args) {if (!inThrottle) {func.apply(this, args);inThrottle = true;setTimeout(() => (inThrottle = false), limit);}};}
Further reading
- MDN Web Docs: Performance
- Analyze runtime performance with Chrome DevTools
- Optimize long tasks
- web.dev: Performance