Next.js

Verified against Claude Code · 2026-07-26

Audit a page for bundle bloat before reaching for next/dynamic as a reflex fix

Traces a bundle-analyzer report back to specific imports and asks whether the real fix is converting a component to a Server Component, dynamically importing it, or replacing an oversized dependency — instead of wrapping everything in next/dynamic by default.

Claude CodeCursorGitHub Copilot3 fillable variables

The prompt

Ready to copy — highlighted parts are example details you can swap.

You are auditing a page's client bundle size using the output of a bundle analyzer, tracing specific large entries back to the actual import responsible, and matching each finding to the fix that's genuinely correct for its cause rather than reaching for next/dynamic as a default response to every large entry.

BUNDLE ANALYZER OUTPUT
First load JS for /dashboard is 340KB. Largest contributors: a charting library (110KB) inside DashboardPage, a rich-text editor (95KB) inside a 'CommentEditor' component, and a date-utility library (40KB) imported via its default barrel export for two date-formatting functions

PAGE CODE
app/dashboard/page.tsx, marked 'use client' at the top even though it only renders static layout and passes props down; it imports <RevenueChart> (the charting library) and <CommentEditor> (the rich-text editor, only rendered once a 'Add comment' button is clicked)

USER-FACING TIMING NEEDS
Clicking a data point on RevenueChart to drill into detail should feel instant, since it's used constantly during a live walkthrough with stakeholders; opening CommentEditor has no such requirement

AUDIT RULES
For every large entry in the bundle analyzer output, trace it back to the specific import statement responsible, not just the file it appears in — a large chunk attributed to a page file is very often actually one specific import inside that file, and the fix differs completely depending on which import it is. If the large entry is a Client Component that's marked 'use client' but doesn't actually meet any of the real triggers for needing the client — no hooks, no event handlers, no browser APIs — the fix is removing the directive and letting it render as a Server Component, which removes its JavaScript from the client bundle entirely rather than deferring when that JavaScript loads; this is a different and better fix than dynamically importing something that never needed to be client-side JavaScript at all. If the large entry is a genuinely client-side dependency that's only needed for a specific, deferred interaction — a modal that opens on a rare click, a rich-text editor that's only needed once the user starts actually editing — use next/dynamic with a loading fallback to defer loading that specific chunk until the interaction that actually needs it occurs, rather than including it in the initial bundle for every visitor regardless of whether they ever trigger that interaction. If the large entry is a full library import where only one or two specific functions are actually used, check whether that library supports named, tree-shakeable imports, and whether the current import statement is written in a way that actually allows tree-shaking to work — importing from a library's barrel file (its main index re-exporting everything) can silently defeat tree-shaking even when the code only references one export, because the bundler may not be able to prove the rest of the barrel file's side effects are safe to drop, whereas importing directly from the specific submodule path usually resolves this. If the timing needs above state that a specific interaction must feel instant with no visible loading delay, do not recommend next/dynamic for the component behind that specific interaction without also addressing how its fallback state is handled, since a dynamically imported component still has to load before it can render, and an interaction that's supposed to feel instant now has a real, if brief, loading window that wasn't there when the code shipped eagerly.

OUTPUT FORMAT
A table: Bundle entry | Actual import responsible | Root cause (unnecessary client boundary, deferred-interaction candidate, un-tree-shaken library import) | Recommended fix. Close with one paragraph naming the single largest opportunity and its estimated bundle-size impact if the analyzer output includes size figures.

Customize

Optional — swap in your own details for the highlighted parts above.

Why this works

Tracing a large bundle entry back to the specific import responsible, rather than stopping at the file it appears in, is what prevents the single most common mistake in a bundle-size fix: applying next/dynamic to an entire page file when the actual weight is concentrated in one import inside it, which either fails to help because the rest of the file still loads eagerly, or helps by accident while leaving the real, reusable lesson — which specific dependency was actually heavy — undiscovered for the next page that imports the same thing. Distinguishing an unnecessary client boundary from a genuinely client-side but deferrable dependency matters because these have completely different correct fixes: a component wrongly marked 'use client' with no real trigger for needing the browser should simply become a Server Component, which removes its JavaScript from the client bundle entirely and doesn't even show up as a chunk to defer, while a genuinely client-only dependency that's only needed for a specific interaction is exactly what next/dynamic is for — reaching for next/dynamic on the first case is a working but wasteful fix, since the code still ships to the browser, just slightly later, when it could have not needed to ship there at all. The barrel-file tree-shaking detail closes a gap that looks correct in the source code and only shows up in the bundle analyzer's actual output: importing one named function from a library's main index file can still pull in the whole barrel's contents if the bundler can't prove the unused exports have no side effects worth preserving, which means a developer who wrote what looks like a minimal, single-function import can still be shipping the entire library, and the fix — importing from the specific submodule path instead of the barrel — is invisible from reading the import statement alone unless the actual bundle output is checked. Tying the recommendation for the drill-down interaction to its explicit instant-feel requirement, rather than treating next/dynamic as a universal free win, is what stops a well-intentioned bundle-size fix from introducing a small but real and noticeable delay into exactly the interaction the business explicitly cares most about feeling fast — dynamic import genuinely defers load time, and pretending that deferral has zero cost for an interaction that's used constantly in front of stakeholders would be trading one measured problem for a different, unmeasured one.

What you get back

Bundle entry | Import | Root cause | Fix DashboardPage wrapper (part of the 340KB) | 'use client' on the page itself, no real trigger | Unnecessary client boundary | Remove 'use client' from the page; keep it on RevenueChart and CommentEditor only, where it's actually needed CommentEditor (95KB) | Rich-text editor, only rendered after a button click | Deferred-interaction candidate | next/dynamic with a small loading fallback — no instant-feel requirement stated for opening it date-utility import (40KB for 2 functions) | Barrel import (import { formatDate, parseDate } from 'date-lib') | Un-tree-shaken library import | Import from 'date-lib/format' and 'date-lib/parse' directly if the library exposes submodule paths, instead of the barrel Largest opportunity: removing the unnecessary 'use client' from DashboardPage itself doesn't reduce RevenueChart's 110KB (it's needed, and its drill-down interaction has a stated instant-feel requirement, so it should stay eager, not dynamic), but combined with fixing the date-utility barrel import, this removes roughly 40KB+ from the initial bundle with no functional change and no new loading state to manage.

Verified against

Claude Code Sonnet 4.6 · 2026-07-26

Cursor Cursor 2.1 · 2026-08-04

Changelog

  • 2026-07-26 Initial publish, verified against Claude Code (Sonnet 4.6) and Cursor 2.1 on a Next.js 16 dashboard page with an @next/bundle-analyzer report.
Pairs with our free Website Speed Test — no signup, runs in your browser.

Need this built into your business?

If a prompt isn't enough — custom software, built and maintained for you — that's Scult's day job.

EXPLORE CUSTOM SOFTWARE
All Next.js prompts

Check your AI visibility

One URL in, a 0–100 score and the exact fixes out.

RUN THE CHECK

Browse all the tools

15 tools across six categories
13 of them never send your data anywhere

Free · No signup · No trial clock

SEE THE DIRECTORY