Quick reference checklist for web application performance. Use alongside the performance-optimization skill.
| Metric | Good | Needs Work | Poor |
|---|---|---|---|
| LCP (Largest Contentful Paint) | ≤ 2.5s | ≤ 4.0s | > 4.0s |
| INP (Interaction to Next Paint) | ≤ 200ms | ≤ 500ms | > 500ms |
| CLS (Cumulative Layout Shift) | ≤ 0.1 | ≤ 0.25 | > 0.25 |
- Images use modern formats (WebP, AVIF)
- Images are responsively sized (
srcsetandsizes) - Images have explicit
widthandheightattributes (prevents CLS) - Below-the-fold images use
loading="lazy" - Hero/LCP images use
fetchpriority="high"and no lazy loading
- Bundle size under 200KB gzipped (initial load)
- Code splitting with dynamic
import()for routes and heavy features - Tree shaking enabled (no dead code in production bundle)
- No blocking JavaScript in
<head>(usedeferorasync) - Heavy computation offloaded to Web Workers (if applicable)
-
React.memo()on expensive components that re-render with same props -
useMemo()/useCallback()only where profiling shows benefit
- Critical CSS inlined or preloaded
- No render-blocking CSS for non-critical styles
- No CSS-in-JS runtime cost in production (use extraction)
- Font display strategy set (
font-display: swaporoptional) - System font stack considered before custom fonts
- Static assets cached with long
max-age+ content hashing - API responses cached where appropriate (
Cache-Control) - HTTP/2 or HTTP/3 enabled
- Resources preconnected (
<link rel="preconnect">) for known origins - No unnecessary redirects
- No layout thrashing (forced synchronous layouts)
- Animations use
transformandopacity(GPU-accelerated) - Long lists use virtualization (e.g.,
react-window) - No unnecessary full-page re-renders
- No N+1 query patterns (use eager loading / joins)
- Queries have appropriate indexes
- List endpoints paginated (never
SELECT * FROM table) - Connection pooling configured
- Slow query logging enabled
- Response times < 200ms (p95)
- No synchronous heavy computation in request handlers
- Bulk operations instead of loops of individual calls
- Response compression (gzip/brotli)
- Appropriate caching (in-memory, Redis, CDN)
- CDN for static assets
- Server located close to users (or edge deployment)
- Horizontal scaling configured (if needed)
- Health check endpoint for load balancer
# Lighthouse CLI
npx lighthouse https://localhost:3000 --output json --output-path ./report.json
# Bundle analysis
npx webpack-bundle-analyzer stats.json
# or for Vite:
npx vite-bundle-visualizer
# Check bundle size
npx bundlesize
# Web Vitals in code
import { onLCP, onINP, onCLS } from 'web-vitals';
onLCP(console.log);
onINP(console.log);
onCLS(console.log);| Anti-Pattern | Impact | Fix |
|---|---|---|
| N+1 queries | Linear DB load growth | Use joins, includes, or batch loading |
| Unbounded queries | Memory exhaustion, timeouts | Always paginate, add LIMIT |
| Missing indexes | Slow reads as data grows | Add indexes for filtered/sorted columns |
| Layout thrashing | Jank, dropped frames | Batch DOM reads, then batch writes |
| Unoptimized images | Slow LCP, wasted bandwidth | Use WebP, responsive sizes, lazy load |
| Large bundles | Slow Time to Interactive | Code split, tree shake, audit deps |
| Blocking main thread | Poor INP, unresponsive UI | Use Web Workers, defer work |
| Memory leaks | Growing memory, eventual crash | Clean up listeners, intervals, refs |