Module P-11·25 min read

Bundle analysis, dynamic() for code splitting, advanced Image configuration with remotePatterns and custom loaders, Script strategy deep dive, optimizePackageImports for barrel files, and memory leak detection.

JJS
Written by Jatin Jain Saraf · Senior Software Engineer

P-11 — Performance Optimisation for Practitioners

Who this is for: Engineers who have deployed a Next.js app and are now staring at Lighthouse scores or slow load times and wondering where to start. This module is a structured approach to Next.js performance — how to audit, what to fix first, and what "optimized" actually looks like at the end.


Start With the Build Output

Before running Lighthouse, before installing a bundle analyzer, read the build output. next build prints a table that tells you exactly how Next.js categorized every route in your app:

text

The symbols matter. is fully static — best performance, served from CDN, zero server compute. is statically generated with data — good performance, generated at build time. λ is dynamic — server-rendered on every request, hits your server for every page load.

Your job is to make as many routes or as possible and only use λ where you genuinely need per-request data. The most common mistake is accidentally making routes dynamic by calling cookies(), headers(), or Date.now() in a component that didn't need to be dynamic.

If you see a route you expected to be static showing as λ, trace upward through the component tree. Something in the render path is opting into dynamic rendering. Use export const dynamic = 'force-static' to make Next.js throw an error during build if something tries to go dynamic — this surfaces the culprit immediately.


Bundle Analyzer Setup

The bundle analyzer shows you exactly what's in your JavaScript bundles and what's making them large. Setup is two steps:

bash
ts

Run it with ANALYZE=true npm run build. Two HTML files open in your browser — client.html and server.html — showing treemaps of your bundles. Client is what users download; server is what runs in Node.js (less critical for end-user performance, more critical for cold start times).

Look for the large rectangles. Common offenders: moment.js (replace with date-fns or Intl), lodash without tree-shaking (replace with individual lodash-es imports), large chart libraries loaded on pages that have a small chart, icon libraries where you're importing all 1000 icons to use 5, and faker.js accidentally included in production (yes, this happens).


dynamic() for Code Splitting

next/dynamic is a wrapper around React.lazy with server-rendering capabilities. You use it to defer the loading of a component until it's actually needed:

tsx

The ssr: false option is specifically for libraries that break on the server — they reference window, document, or navigator at module load time before React hydration. Common culprits: react-pdf, canvas-based libraries, WebGL wrappers, and some mapping libraries.

A practical rule: any component that requires a library over 50KB and isn't visible in the initial viewport should be dynamically imported. The page renders faster, and users who never trigger that component never download it.


Barrel Files Are Killing Your Bundle

Sign in to keep reading

The rest of this module is free — sign in with Google to unlock it and track your progress.

Sign in & Register

Discussion

0

Join the discussion

Loading comments...

© 2026 Jatin Jain Saraf (JJS). All rights reserved.