Web Performance Optimization
Systematic approach: Measure → Identify → Prioritize → Implement → Verify.
Target Metrics
| Metric |
Good |
Needs Work |
Poor |
| LCP |
< 2.5s |
2.5-4s |
> 4s |
| INP |
< 200ms |
200-500ms |
> 500ms |
| CLS |
< 0.1 |
0.1-0.25 |
> 0.25 |
| TTFB |
< 800ms |
800ms-1.8s |
> 1.8s |
Quick Wins
1. Images (usually biggest impact on LCP)
<!-- Hero/LCP image: eager + high priority -->
<img src="/hero.webp" alt="Hero" width="1200" height="600" loading="eager" fetchpriority="high" decoding="async" />
<!-- Below fold: lazy load -->
<img src="/product.webp" alt="Product" width="400" height="300" loading="lazy" decoding="async" />
Always set width and height to prevent CLS.
2. Fonts (common LCP/CLS culprit)
<!-- Preconnect to font origin -->
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<!-- Non-blocking font load -->
<link
rel="stylesheet"
href="https://fonts.googleapis.com/css2?family=Inter&display=swap"
media="print"
onload="this.media='all'"
/>
3. Third-party Scripts (common INP killer)
<!-- Defer to user interaction -->
<script>
function loadThirdParty() {
// Load analytics, chat widgets, etc.
}
;['scroll', 'click', 'touchstart'].forEach((e) => addEventListener(e, loadThirdParty, { once: true, passive: true }))
setTimeout(loadThirdParty, 5000)
</script>
4. Critical CSS
Inline critical CSS in <head>, defer the rest:
<style>
/* critical styles */
</style>
<link rel="preload" href="/styles.css" as="style" onload="this.rel='stylesheet'" />
Bundle Analysis
# Webpack
npx webpack-bundle-analyzer dist/stats.json
# Vite
npx vite-bundle-visualizer
# Check package size before installing
npx bundlephobia <package-name>
Common heavy packages to replace:
moment (67KB) → date-fns (12KB) or dayjs (2KB)
lodash (72KB) → cherry-pick imports or native methods
Code Splitting Patterns
// React lazy
const Chart = lazy(() => import('./Chart'))
// Next.js dynamic
const Admin = dynamic(() => import('./Admin'), { ssr: false })
// Vite/Rollup manual chunks
build: {
rollupOptions: {
output: {
manualChunks: {
vendor: ['react', 'react-dom']
}
}
}
}
Caching Headers
# Static assets (immutable hash in filename)
Cache-Control: public, max-age=31536000, immutable
# HTML (revalidate)
Cache-Control: no-cache
# API responses
Cache-Control: private, max-age=0, must-revalidate
Measurement
For running audits, reading reports, and setting budgets, use the perf-lighthouse skill.
Checklist
Images
JavaScript
CSS
Fonts
Detailed Examples
For in-depth optimization patterns, see:
- [references/core-web-vitals.md](references/core-web-vitals.md) - Fixing LCP, CLS, INP issues
- [references/bundle-optimization.md](references/bundle-optimization.md) - Reducing JS bundle size
- [references/image-optimization.md](references/image-optimization.md) - Image formats, responsive images, sharp scripts