Before we dive in, why not take a quick peek at my portfolio: https://priyalraj.com
📋 TL;DR: What You’ll Learn Here
Turbopack import tracing: find why optional features reach initial client bundles.
next-bundle-optimizer: give your coding agent evidence before asking for changes.
Before/after snapshots: separate measured improvements from convincing explanations.
Browser checks: preserve loading states, interactions, & actual user experience.
📦 Why This Next.js Update Matters
Next.js announced its agent-assisted bundle workflow on October 28, 2026. Next.js 16.4 adds exportable analyzer data & an official next-bundle-optimizer skill. Requires Turbopack; it investigates module relationships rather than guessing from package names.
My interest: finding imports that look harmless during code review. Search utility imports shared helper; helper imports feature registry; registry imports editor. Suddenly visitors download editing code before opening anything.
Think of packing for work. Need laptop & charger. Shared checklist also packs camping equipment because somebody reused their holiday list. Removing individual socks won’t solve that problem. Find why entire camping bag entered your luggage.
Same reasoning applies here. Start with import path, intended interaction, & measurable baseline. Small code change can matter more than replacing entire library.
No promised percentage improvement below. Your app needs its own measurements. Vendor examples establish possibility, not your result.
🔎 Start With One User Journey
I’d choose one route with clear purpose: product page, dashboard, article, or onboarding screen. Write down what visitors must do immediately & what can wait.
For dashboard: navigation & summary cards matter first. Export dialog can wait until export button gets clicked. Main chart might be essential; settings editor probably isn’t. Those decisions require product knowledge, not bundle rectangles.
Record baseline commit, runtime, package manager, lockfile, build env, route, & test account. Keep these consistent while comparing changes. Otherwise dependency upgrade or different account permissions can explain apparent improvement.
My tip: Give optimization ticket one acceptance condition: reduce unnecessary initial client JavaScript on selected route while preserving its documented interactions. That keeps attractive but unrelated rewrites outside scope.
Ask teammate which feature breaks most easily. Include that feature in manual checks. Perf work becomes expensive when savings hide behavior regression.
🛠️ Capture Data Before Changing Imports
Next.js package-bundling docs document interactive analysis, saved output, & JSON Lines export. Run from Next.js app directory with installed compatible version:
# terminal, inside your Next.js app
npx next analyze --output --snapshot before
npx next analyze export --snapshot before > bundle-before.jsonlFor interactive inspection:
# terminal, inside your Next.js app
npx next analyzeAnalyzer lets you inspect route-specific modules & trace import chains. Choose client environment when investigating browser downloads; server findings answer different questions.
Snapshot names make evidence easier to compare. Keep original export separate from later results. Don’t overwrite baseline while experimenting.
Check installed CLI help against official reference, especially when app version differs from current docs:
# terminal, inspect your installed CLI
npx next analyze --help
npx next analyze export --helpAnalysis isn’t deployable production output. next analyze does not replace next build. Treat captured graph as diagnostic evidence, then run normal production build before browser verification.
🤖 Give Agent Narrow Instructions
Install official skill using documented command:
# terminal, add official Next.js agent skill
npx skills add vercel/next.js --skill next-bundle-optimizerSkill instructions distinguish audit from fix mode. Audit produces artifacts & recommendations; explicit fix request authorizes source changes. Start with report:
Use next-bundle-optimizer in audit mode for /dashboard.
Find large modules included in initial client loading.
For each candidate, show complete import chain, owning feature,
proposed boundary change, and behavior requiring verification.
Leave application source, dependencies, and lockfile unchanged.Review result as hypothesis. Ask why module belongs elsewhere. “Large” alone isn’t sufficient: essential editor on editing route deserves its bytes.
Then approve one concrete change through your normal workflow. Suggested follow-up:
Fix only the optional export panel identified in the audit.
Preserve export output, permissions, keyboard access, and errors.
Capture an after snapshot and compare the selected route.
Report changed import paths and any unverified interactions.This gives reviewer small, understandable diff. If agent proposes removing feature, changing auth, or replacing package, evaluate that separately against original requirement.
🧩 Why Lazy Loading Sometimes Disappoints
Dynamic import creates opportunity to defer work. Another static import path can still pull same feature into initial graph. Next.js announcement demonstrates this trap: lazy wrapper helps only when eager path no longer keeps heavy module upfront.
Search usages before editing. Check shared exports, registries, layout imports, & other components using same dependency. Keep tracing until graph matches intended boundary.
Lazy-loading docs show conditional Client Component rendering with next/dynamic. Here’s small adaptation using optional help panel:
// app/ui/help-toggle.jsx
'use client';
import { useState } from 'react';
import dynamic from 'next/dynamic';
const HelpPanel = dynamic(() => import('./help-panel'), {
loading: () => <p role="status">Loading help...</p>,
});
export default function HelpToggle() {
const [open, setOpen] = useState(false);
return (
<section>
<button
type="button"
aria-expanded={open}
aria-controls="help-content"
onClick={() => setOpen((value) => !value)}
>
{open ? 'Hide help' : 'Show help'}
</button>
<div id="help-content">{open && <HelpPanel />}</div>
</section>
);
}// app/ui/help-panel.jsx
export default function HelpPanel() {
return <p>Choose your account settings from the profile menu.</p>;
}Tiny panel illustrates boundary; realistic savings require substantial deferred dependency. Don’t add dynamic wrappers everywhere for cosmetic consistency.
Also distinguish code splitting from on-demand rendering. Rendering dynamically imported component immediately still asks for it immediately. Optional interaction provides useful deferral point.
Next.js documents additional restrictions: ssr: false belongs in Client Components, & dynamically importing Client Component from Server Component currently lacks automatic code splitting. Follow actual rendering context.
🧱 Move Client Boundary Closer to Interaction
Server & Client Component docs explain how 'use client' establishes module boundary. Imported modules become part of client graph. Put directive on interactive leaf instead of broad layout where possible.
Example review question: does entire account page need client state, or only preference toggle? Keep data formatting & static content outside interactive island when architecture allows it.
Moving logic server-side still needs review. Check props crossing boundary, secret handling, & differences between initial render & later interaction. Smaller client output doesn’t excuse incorrect data exposure.
| Finding | Candidate change | Proof needed |
|---|---|---|
| Optional feature loaded initially | Conditional dynamic import | Feature works when first opened |
| Static content inside broad client module | Narrow client boundary | Rendered output & interaction match |
| Heavy utility reached through shared file | Separate lightweight module | All callers receive same values |
| Duplicate dependency versions | Review compatible dependency resolution | Build, tests, & runtime behavior pass |
These are review candidates, not automatic replacements. Keep change tied to evidence from graph.
🎯 Pick Changes Worth Maintaining
I’d rank findings by reach, wasted work, & review cost. Shared import affecting every public route deserves attention before tiny dependency used on rarely visited admin screen. Still inspect actual route data before choosing.
Consider ownership too. Splitting helper into clear module can improve maintenance. Scattering dynamic imports across unrelated components can make future changes harder. Prefer boundary matching feature already understood by team.
Write down deferred feature’s new loading behavior before merging. Who owns loading message? What happens when connection drops? Can user retry without losing form state? Answer those questions while diff remains small.
Keep unsuccessful experiments documented briefly. If proposed split saved negligible bytes or made interaction worse, revert experiment & record why. That prevents next optimization pass from repeating same attractive suggestion.
🧪 Verify Bytes & Behavior Together
Capture fresh analysis after chosen change:
# terminal, preserve separate before/after evidence
npx next analyze --output --snapshot after
npx next analyze export --snapshot after > bundle-after.jsonl
npx next build
npx next startProduction checklist recommends production build & production server for realistic assessment. Dev mode isn’t comparison baseline.
Use Chrome Network panel with cache disabled while DevTools remains open. Keep throttling settings identical. Load route directly, inspect initial JavaScript, then trigger deferred feature.
✅ Confirm module left initial path, then arrives when needed.
✅ Test first opening, closing, reopening, & slow loading.
✅ Test empty data, permission restrictions, & failed requests.
❌ Don’t call smaller total build directory proof of faster route.
❌ Don’t compare warm cached page with uncached baseline.
❌ Don’t treat loading fallback as complete error recovery.
For React.lazy, rejected imports reach nearest Error Boundary; React docs explain loading & error handling separately. Review failure behavior wherever you introduce asynchronous loading.
Write PR evidence plainly: route checked, module deferred, measured sizes, build status, & interaction checks. If improvement disappears in browser, investigate before claiming win.
Final Thoughts
Next.js bundle optimization works best when import graph & product intent agree. Agent can trace paths; reviewer decides whether feature belongs on initial journey.
Start with one route, preserve baseline, fix one boundary, & inspect production behavior. Keep changes that make experience better. Leave essential code where users need it. 🙂
If you enjoyed this article, please make sure to follow me on Twitter.



Priyal Raj
Full Stack Developer with 3+ years building production web apps for founders and product teams worldwide. I write about Next.js, Node.js, TypeScript, Redis, and everything in between.