| name | js-perf |
| description | JS performance patterns |
Addy Osmani JavaScript Philosophy
Applying Addy Osmani's JavaScript performance optimization philosophy, design patterns, and web vitals expertise to frontend development. Use when optimizing page load times, implementing lazy loading, improving Core Web Vitals scores, applying JavaScript design patterns, optimizing images, implementing code splitting, or when performance budgets are discussed. Not for backend optimization, database queries, or non-JavaScript performance work.
Core Philosophy
Performance is User Experience: Every millisecond of delay costs engagement. Measure real user metrics, not synthetic benchmarks.
Progressive Enhancement: Start with the minimal viable experience, enhance with JavaScript. Never require JS for core content.
Ship Less JavaScript: The fastest code is the code you don't ship. Question every dependency, every abstraction, every byte.
Performance Optimization Hierarchy
1. Loading Strategy (Critical Path)
Prioritize Above-the-Fold Content
<style></style>
<link rel="preload" href="critical.js" as="script">
<link rel="preload" href="hero.webp" as="image">
<script defer src="analytics.js"></script>
Resource Hints
<link rel="dns-prefetch" href="//cdn.example.com">
<link rel="preconnect" href="https://fonts.googleapis.com" crossorigin>
<link rel="prefetch" href="/likely-next-page.html">
<link rel="modulepreload" href="/critical-module.js">
2. Code Splitting Patterns
Route-Based Splitting (React)
const Dashboard = React.lazy(() => import('./Dashboard'));
const Settings = React.lazy(() => import('./Settings'));
function App() {
return (
<Suspense fallback={<Skeleton />}>
<Routes>
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/settings" element={<Settings />} />
</Routes>
</Suspense>
);
}
Magic Comments for Webpack
import( 'lodash');
import( './HeavyComponent');
import( './CriticalComponent');
3. Image Optimization
Modern Formats with Fallbacks
<picture>
<source srcset="hero.avif" type="image/avif">
<source srcset="hero.webp" type="image/webp">
<img src="hero.jpg" alt="Hero" loading="lazy" decoding="async">
</picture>
Responsive Images
<img
srcset="small.jpg 400w, medium.jpg 800w, large.jpg 1200w"
sizes="(max-width: 600px) 400px, (max-width: 1000px) 800px, 1200px"
src="medium.jpg"
alt="Responsive image"
loading="lazy"
decoding="async"
>
Native Lazy Loading
<img src="photo.jpg" loading="lazy" alt="...">
<img src="hero.jpg" loading="eager" fetchpriority="high" alt="...">
4. Core Web Vitals Optimization
Largest Contentful Paint (LCP) < 2.5s
- Preload LCP image
- Inline critical CSS
- Remove render-blocking resources
- Use CDN for static assets
First Input Delay (FID) < 100ms / Interaction to Next Paint (INP) < 200ms
function processLargeArray(items) {
const CHUNK_SIZE = 100;
let index = 0;
function processChunk() {
const chunk = items.slice(index, index + CHUNK_SIZE);
chunk.forEach(processItem);
index += CHUNK_SIZE;
if (index < items.length) {
requestIdleCallback(processChunk);
}
}
processChunk();
}
Cumulative Layout Shift (CLS) < 0.1
.image-container {
aspect-ratio: 16 / 9;
width: 100%;
}
.ad-slot {
min-height: 250px;
}
5. JavaScript Design Patterns
Module Pattern (Encapsulation)
const ShoppingCart = (() => {
let items = [];
const calculateTotal = () => items.reduce((sum, item) => sum + item.price, 0);
return {
add(item) { items.push(item); },
remove(id) { items = items.filter(i => i.id !== id); },
getTotal() { return calculateTotal(); },
getItems() { return [...items]; }
};
})();
Observer Pattern (Pub/Sub)
class EventBus {
#subscribers = new Map();
subscribe(event, callback) {
if (!this.#subscribers.has(event)) {
this.#subscribers.set(event, new Set());
}
this.#subscribers.get(event).add(callback);
return () => this.#subscribers.get(event).delete(callback);
}
publish(event, data) {
this.#subscribers.get(event)?.forEach(cb => cb(data));
}
}
6. Runtime Performance
Debounce & Throttle
const debounce = (fn, delay) => {
let timeoutId;
return (...args) => {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => fn(...args), delay);
};
};
const throttle = (fn, limit) => {
let inThrottle;
return (...args) => {
if (!inThrottle) {
fn(...args);
inThrottle = true;
setTimeout(() => inThrottle = false, limit);
}
};
};
Memoization
function memoize(fn) {
const cache = new Map();
return (...args) => {
const key = JSON.stringify(args);
if (cache.has(key)) return cache.get(key);
const result = fn(...args);
cache.set(key, result);
return result;
};
}
7. Bundle Optimization
Tree Shaking Best Practices
import _ from 'lodash';
_.debounce(fn, 100);
import debounce from 'lodash/debounce';
debounce(fn, 100);
function debounce(fn, delay) { }
Performance Budgets
module.exports = {
performance: {
maxAssetSize: 250000,
maxEntrypointSize: 250000,
hints: 'error'
}
};
Measurement & Monitoring
Real User Monitoring
import { onCLS, onFID, onLCP, onINP, onTTFB } from 'web-vitals';
function sendToAnalytics({ name, value, id }) {
navigator.sendBeacon('/analytics', JSON.stringify({ name, value, id }));
}
onCLS(sendToAnalytics);
onLCP(sendToAnalytics);
onINP(sendToAnalytics);
Anti-Patterns to Avoid
- Layout Thrashing: Batch DOM reads before writes
- Forced Synchronous Layouts: Avoid reading layout props after writes
- Memory Leaks: Clean up event listeners, timers, subscriptions
- Blocking the Main Thread: Use Web Workers for heavy computation
- Render Blocking Resources: Defer non-critical JS/CSS
- Over-Engineering: Simple solutions before patterns
- Premature Optimization: Measure first, optimize second
Performance Checklist