Debouncing, lazy loading, virtualization, and other patterns that keep web apps fast as they grow.
Performance Is a Feature
Users notice when things feel slow. A 100ms delay feels instant. 300ms feels noticeable. Over 1 second feels broken. These patterns help you stay in the fast zone.
Debouncing and Throttling
Search inputs, window resize handlers, scroll events — all fire many times per second. Debounce waits until the user stops, throttle limits to N calls per second.
Debounce for search input (wait until user stops typing). Throttle for scroll events (run at most every 100ms).
Lazy Loading
Do not load what the user cannot see. Images below the fold, components behind tabs, routes the user has not navigated to — all should load on demand.
The loading="lazy" attribute on images handles the most common case. For components, use dynamic imports.
Virtual Scrolling
Rendering 10,000 DOM nodes kills performance. Virtual scrolling renders only the visible items plus a small buffer. Libraries like react-window or @tanstack/virtual handle this.
The threshold is roughly 500+ items. Below that, the DOM handles it fine.
Web Workers for Heavy Computation
Parsing large JSON files, image processing, complex calculations — move them off the main thread. Web Workers run in a separate thread and do not block the UI.
RequestAnimationFrame for Animations
Never animate with setInterval. requestAnimationFrame syncs with the browser's refresh rate, giving smooth 60fps animations without wasted frames.
Memoization
Cache expensive function results. If the same inputs produce the same output, compute it once and store it. useMemo and React.memo are React-specific, but the pattern applies everywhere.
Only memoize when you have measured a performance problem. Premature memoization adds complexity without benefit.
Batch DOM Updates
Multiple DOM reads followed by writes cause layout thrashing. Batch all reads together, then all writes. Or use requestAnimationFrame to schedule DOM updates.
Measuring Before Optimizing
Chrome DevTools Performance tab, performance.mark(), and performance.measure() tell you where time is actually spent. Optimizing without measuring is guessing.