Performance is not one thing — it's the sum of dozens of small decisions. This checklist covers 30 items across six areas: images, fonts, JavaScript, CSS, caching, and rendering. Each item is ranked by typical impact. Tackle the top items first.
Images (highest impact)
- Convert all images to WebP or AVIF. WebP cuts JPEG by 25–35% and PNG by 50–70%. Use Picovert's converter for static assets.
- Never lazy-load the LCP image. Add
fetchpriority="high"and a<link rel='preload'>in<head>instead. - Always specify width and height on every img. Prevents CLS from images loading without reserved space.
- Use srcset with at least 3 sizes. 320w, 768w, 1200w covers mobile, tablet, and desktop. Cuts mobile payload by 50–60%.
- Lazy-load below-the-fold images.
loading="lazy"is now baseline in all modern browsers. Zero JavaScript needed. - Convert GIF animations to MP4. MP4 is 10–20x smaller and uses hardware decoding. Convert here.
Fonts
- Use WOFF2 only. WOFF2 has 30% better compression than WOFF and is supported by 98% of browsers.
- Subset fonts to characters you actually use. A full Latin font is 80 KB. Subset to your language's character set: 15–20 KB.
- Preload critical fonts.
<link rel="preload" as="font" crossorigin>removes the second-round-trip discovery delay. - Set font-display: swap on body fonts, optional on decorative fonts. Prevents invisible text during load (FOIT) while avoiding major layout shifts.
- Self-host fonts instead of loading from Google Fonts. Eliminates a DNS lookup and connection to fonts.googleapis.com.
JavaScript
- Split code at the route level. Each route should only load the JavaScript it needs. Next.js does this automatically for pages.
- Audit and remove unused dependencies. Run
npx bundle-phobia [package]before adding any new npm package. - Dynamic import heavy components. Rich text editors, chart libraries, PDF renderers — load them only when needed.
- Replace moment.js with date-fns or dayjs.moment.js is 67 KB; date-fns is tree-shakeable to <5 KB for most use cases.
- Defer all non-critical third-party scripts. Analytics, heatmaps, chatbots — load after the
loadevent fires. - Enable React's production build. Development mode is 2–3x larger and slower. Verify with React DevTools profiler.
CSS
- Inline critical CSS for above-the-fold content. The browser can render the visible portion without waiting for external stylesheets.
- Remove unused CSS. Use PurgeCSS or Tailwind's JIT to ship only what your pages actually use.
- Use contain: layout on isolated components. CSS containment prevents style recalculations from propagating across the tree.
- Use will-change sparingly. Overuse promotes too many elements to compositor layers, consuming GPU memory.
- Prefer CSS animations over JavaScript animations. CSS animations run on the compositor thread and don't block the main thread.
Caching
- Set long-lived cache headers on static assets.
Cache-Control: max-age=31536000, immutablefor hashed filenames (JS, CSS, images). Immutable tells the browser not to revalidate. - Enable stale-while-revalidate for HTML. Serves the cached HTML immediately while fetching the update in the background.
- Deploy to a CDN with edge nodes close to users.Reduces round-trip latency from 150 ms to <10 ms for most global traffic.
- Configure ETag or Last-Modified on dynamic assets. Allows 304 Not Modified responses, saving bandwidth on unchanged resources.
Rendering
- Use Next.js App Router RSC for data-heavy pages. Server Components stream HTML without shipping their own JavaScript to the client.
- Streaming HTML with Suspense boundaries. Users see content progressively instead of waiting for the slowest data dependency.
- Minimize hydration scope. Only hydrate the interactive parts of a page. Static content should stay server-rendered.
- Use content-visibility: auto on off-screen sections. The browser skips layout and paint for invisible content, cutting rendering time by 40–60% on long pages.
- Measure, don't guess. Run Lighthouse, check CrUX data in Search Console, and use the web-vitals library with your own RUM data before and after each change.
Priority matrix
| Area | Max LCP gain | Max INP gain | Max CLS gain |
|---|---|---|---|
| Images | High | Low | High (size attributes) |
| Fonts | Medium | Low | High (font-display) |
| JavaScript | Medium | High | Low |
| CSS | Medium | Medium | Low |
| Caching | High (repeat visits) | Low | Low |
| Rendering | High (streaming) | High (hydration) | Low |