Open the Network tab on any production Next.js app and you'll see a pile of JavaScript files with hashed names. Those are chunks, and the way your bundler decides what goes into each one has a direct effect on how fast your pages load and how fast navigation feels. The Next.js team recently published a deep dive into how Turbopack makes these decisions, and Next.js 16.3 ships a set of experimental options that let you tune it. Here's what I took away, and how I'd try it on a real project.
The Core Tradeoff: Requests vs. Bytes
There are three naive ways to chunk an app, and each one fails differently:
- One chunk for everything – great cache hits after the first load, but every page downloads code for every other page.
- One chunk per page – no over-shipping, but shared components like a footer get downloaded again on every page.
- One chunk per module – perfect reuse, but hundreds of tiny requests, each with overhead, and worse compression because gzip/brotli can only find patterns inside a single file.
Turbopack's approach sits in the middle: it starts small and merges chunks within a chunk group. A chunk group is the set of chunks a route always loads together, so merging inside a group can never add code the page wasn't already downloading.
The interesting part is when merging pays off. If a visitor lands on one page and leaves, merging always wins (fewer requests, same bytes). If they navigate to a second page that only needs part of a merged chunk, the merged file is useless and some code gets downloaded twice. Turbopack weighs these scenarios by probability, and by default it assumes roughly two-thirds of sessions are single-page visits.
On nextjs.org, the team measured an eight-page session: the defaults cut requests from 96 to 38 compared with no merging, while shipping slightly less JavaScript. Merging everything per group dropped requests to 15 but shipped about 10% more code overall. There's no universally right answer, which is exactly why the new knobs exist.
What's New in Next.js 16.3
All of these live under experimental in next.config, so treat them as opt-in and measure before and after.
1. Component Chunks for Smarter Fetching
The bundler decides merging at build time, but only the browser knows what's already cached. With generateComponentChunks enabled, Turbopack emits un-merged versions of chunks alongside the merged ones. At runtime it tracks which pieces are already loaded and fetches whichever is cheaper: the merged chunk or just the missing parts. It works in reverse too, skipping a piece that already arrived inside a merged chunk.
2. Analytics-Based Tuning
If you know how people move through your site, you can tell Turbopack instead of relying on its guesses:
firstPageLoadPriority– a value between 0 and 1 (default0.67) that shifts weight toward a fast first load versus cheaper navigation. Your bounce rate is a sensible starting point.priorityRoutes– routes whose load speed matters most; chunks on these get merged more aggressively.clusters– groups of routes that are commonly visited together, defined with regular expressions, so overlapping chunks inside a cluster merge more readily.
3. Shipping Less Code
turbopackCjsTreeShakingtree-shakes CommonJS modules, not just ESM, so unused CJS exports stop reaching the client.turbopackSharedRuntimereplaces per-page runtime chunks with a single shared runtime, saving a blocking request and roughly 10 KB on every navigation after the first.- The default runtime also no longer includes WebAssembly and Web Worker loading code unless your app actually uses them.
Trying It Out
Here's a starting configuration for a content site where most visitors arrive from search and leave after one page:
// next.config.ts
import type { NextConfig } from 'next'
const nextConfig: NextConfig = {
experimental: {
turbopackChunking: {
// Emit un-merged chunks too, so soft navigations fetch only what's missing
generateComponentChunks: true,
// High bounce rate: favour the first page load
firstPageLoadPriority: 0.75,
},
turbopackCjsTreeShaking: true,
turbopackSharedRuntime: true,
},
}
export default nextConfig
For priorityRoutes and clusters, check the turbopackChunking reference in the Next.js docs for the exact shape before adding them. Then measure with a repeatable script rather than eyeballing a single load:
# Upgrade, build, and run a production server
npm install next@latest
npx next build
npx next start
# In another terminal, profile a few key routes
npx lighthouse http://localhost:3000/ --only-categories=performance --output=json --output-path=./home.json
npx lighthouse http://localhost:3000/blog --only-categories=performance --output=json --output-path=./blog.json
Lighthouse only measures cold loads, so also record a short navigation session in the Chrome DevTools Network panel (filtered to JS) and compare request counts and transferred bytes before and after each change. Change one option at a time; otherwise you won't know which one helped.
When I'd Reach for Each Option
- Marketing or blog site: raise
firstPageLoadPriorityand mark your landing pages as priority routes. - Dashboard or SaaS app: users navigate a lot, so lower
firstPageLoadPriorityand define clusters for areas like/settingsor/reports. - Any app with older CJS dependencies: try CJS tree-shaking first; it's often the cheapest win.
A Note on Upgrading
Next.js has been shipping frequent security releases lately, including out-of-band updates in September. Staying on a current 16.3.x patch is worth doing regardless of these features, and it makes experimenting with them a one-line change.
Key Takeaways
- Chunking is a balance between fewer requests and fewer bytes; no single strategy wins for every site.
- Turbopack merges chunks only within a chunk group, weighted by how likely different navigation patterns are.
generateComponentChunkslets the runtime choose between merged and un-merged chunks based on what's already cached.firstPageLoadPriority,priorityRoutes, andclusterslet you feed real traffic patterns into the algorithm.- CJS tree-shaking and a shared runtime reduce shipped code; both are expected to become defaults later.
- These options are experimental: measure navigation sessions, not just cold loads, and change one thing at a time.
