Lesson 39 of 81
Reaching for next/dynamic to shrink a bundle only works if you know which import is actually the problem. @next/bundle-analyzer turns 'the app feels slow' into a picture of exactly what's in the client JavaScript and how big each piece is.
12 min read
Every option explains itself — why the right one is right, and why each wrong one is wrong. Miss one and it comes back later on a spaced-repetition schedule.
This topic is about judgment and practical tradeoffs more than a single right answer — a mock interview is the better way to test yourself on it.
Be interviewed on this →Hints without spoilers, and a plain explanation of why a wrong answer was wrong — answered only from this lesson. Free account, no card.
Sign up freeWhat interviewers actually ask about this, and what a strong answer has to cover.
A strong answer covers
Likely follow-ups
@next/bundle-analyzer is an official Next.js plugin that wraps your build and produces a visual treemap of everything that ended up in your JavaScript bundles — one rectangle per module, sized proportionally to how many bytes it contributes. Instead of guessing which dependency is bloating the client bundle, you get an actual picture: a lodash import that isn't tree-shaking, a moment.js locale bundle nobody meant to include, a whole icon library pulled in for three icons.
It installs as a config wrapper around next.config.ts and only activates when you set an environment variable, so it never affects a normal build:
// next.config.ts
import type { NextConfig } from "next";
import createBundleAnalyzer from "@next/bundle-analyzer";
const withBundleAnalyzer = createBundleAnalyzer({
enabled: process.env.ANALYZE === "true",
});
const nextConfig: NextConfig = {};
export default withBundleAnalyzer(nextConfig);
Running ANALYZE=true pnpm build then opens interactive treemaps in the browser — separate reports for the client bundle, the server bundle, and the edge runtime bundle — after the build finishes.
ANALYZE=true pnpm build
Say the client treemap shows a single 180KB rectangle labeled date-fns — far bigger than expected for a "format one date" use case. The likely cause is an import like this:
// BAD: pulls in the entire date-fns package
import * as dateFns from "date-fns";
dateFns.format(new Date(), "PP");
// GOOD: only the functions actually used are bundled
import { format } from "date-fns";
format(new Date(), "PP");
The treemap doesn't fix anything by itself — it's a diagnostic. Once you can see that date-fns is disproportionately large, the fix is usually one of three things: switch to named imports so tree-shaking can drop the unused code, reach for optimizePackageImports in next.config.ts for packages with many named exports, or — if the whole thing is only needed behind an interaction, like a date picker that opens on click — wrap it in next/dynamic so it's not in the initial bundle at all.
next build), not next dev. Dev-mode bundles are shaped completely differently (unminified, unbundled-by-route in places) and aren't representative of what ships to users.ANALYZE=true has to be set on the same command that runs the build, not exported separately in an unrelated shell session — a forgotten ANALYZE=true just produces a normal build with no report.next/dynamic, or accept it because it's genuinely needed everywhere is a judgment call the tool doesn't make for you."Lighthouse flags a large JavaScript payload on your homepage — walk me through how you'd actually find what's causing it." tests whether a candidate has a real diagnostic workflow (run the analyzer, read the treemap, identify the specific offending import) versus guessing at optimizations blind, which is a common gap even among people who know next/dynamic exists as a fix.