Home / Articles / Tuning Turbopack's New Chunking Controls in Next.js 16.3

This article is published in English.

Next.jsTurbopackWebpackPerformanceFrontend

Tuning Turbopack's New Chunking Controls in Next.js 16.3

A practical look at Next.js 16.3's new turbopackChunking config, explaining how maxChunkCountPerGroup and generateComponentChunks affect bundle size and caching.

1450 words

It was 4:40 on a Friday, exactly the moment you're shutting the laptop for the weekend, when a routine staging deploy for a dashboard app quietly stopped being routine. A build that normally finished in 40 seconds took just over three minutes, and the client bundle for the main route grew by nearly 300kb. No routes had been touched. No new dependency had been pulled in that would explain it. The only change that week was a minor Next.js version bump. What followed was an evening spent staring at the Network tab, watching four massive JavaScript files load one after another where a dozen small ones used to do the job.

The cause wasn't a bug. It was Turbopack's chunking logic behaving exactly as designed, just not in a way that matched expectations.

Why Chunking Was Never Really Solved

This is a problem bundlers have been approximating for roughly ten years. Webpack's SplitChunksPlugin exposed settings like minSize, maxSize, and cacheGroups that almost nobody actually understood from first principles, they were mostly copied from some Stack Overflow thread dating back to 2019. The underlying tradeoff never goes away: consolidating into fewer, larger chunks cuts down HTTP requests but hurts caching, since a single CSS change can invalidate a 500kb bundle. Splitting into many smaller chunks improves cache granularity but multiplies round trips, which becomes noticeable fast on a slow connection.

Turbopack inherited that same balancing act, and until recently it defaulted strongly toward consolidation. That's a reasonable choice for a single-page session, where most assets are fetched once anyway. It works against you, though, if users navigate repeatedly through a large application, since previously cached code keeps getting re-downloaded bundled alongside whatever new code happens to sit next to it.

What Changed in 16.3

With Next.js 16.3, the merging behavior is no longer buried inside a single opaque heuristic, it's now surfaced directly through configuration. A new turbopackChunking block has been added, and the two settings that matter most are maxChunkCountPerGroup and maxMergeChunkSize.

// next.config.js
module.exports = {
  experimental: {
    turbopackChunking: {
      maxChunkCountPerGroup: 40, // default
      maxMergeChunkSize: 200_000, // bytes, default
    },
  },
};

Setting maxChunkCountPerGroup lower pushes Turbopack toward more aggressive merging: fewer requests per page load, but weaker cache retention across navigations. Raising it produces smaller, more numerous chunks that stay intact even when unrelated parts of the app change. maxMergeChunkSize caps how big a merged chunk is allowed to grow before Turbopack stops folding additional code into it, which is useful when a single large vendor dependency tends to absorb everything around it.

None of these knobs existed previously. You simply got whatever the default heuristic produced and worked around it.

The Component Chunking Option

A more notable addition is generateComponentChunks, which extends Turbopack's chunking awareness down to individual components rather than stopping at route-level boundaries.

module.exports = {
  experimental: {
    turbopackChunking: {
      generateComponentChunks: true,
    },
  },
};

Enabling this causes Turbopack to output both merged and un-merged versions of chunks, letting the runtime decide at load time which version to fetch based on what the browser has already cached. If a shared component was cached from an earlier page visit, the runtime can request the smaller un-merged chunk instead of pulling in a merged bundle that duplicates code already present. This is a fundamentally different approach than trying to precompute the ideal chunk size ahead of time, it's the first case of a bundler pushing that decision into request time instead of locking it in at build time.

What We Actually Saw

A team testing this on a dashboard application flipped generateComponentChunks on and watched what happened over the following days. The initial page load barely budged, shrinking by roughly 2%. But repeat navigations, which make up the bulk of real usage for an app like a dashboard, saw average JS transferred per navigation fall by about 35%. That's the core value proposition here: this isn't a feature aimed at making your first load lighter, it's aimed at the pattern of someone clicking through your app for ten minutes straight. Most of the bundle-size benchmarks circulating online are measuring an entirely different scenario, one that doesn't reflect how apps with heavy internal navigation actually get used.

The same team also revisited maxChunkCountPerGroup for a marketing site living in the same monorepo, dialing it down from 40 to 24. That site sees mostly single-page sessions, so the extra network requests generated by more granular chunking were pure overhead with no payoff. Same underlying tool, opposite tuning, and both choices were correct for their respective contexts. That's arguably the real lesson: there was never a single universally right chunking strategy, Turbopack just used to make that choice for you without asking.

How This Compares to the Old Webpack Knobs

For anyone who's spent years wrestling with splitChunks.cacheGroups, the closest analog in this new system is maxMergeChunkSize, and unlike a lot of Webpack's naming, this one behaves exactly the way it sounds like it should. Webpack's configuration surface offered a dozen loosely documented settings that interacted with each other in ways nobody had fully mapped, and plenty of teams ended up inheriting a config file from engineers long gone, one that everyone was too nervous to modify. Turbopack's approach is deliberately narrower. You get two or three levers instead of a dozen, which sacrifices some flexibility for genuinely unusual edge cases, but it also shortens the distance between noticing "our bundle looks off" and knowing exactly which number to adjust. That trade-off is a reasonable one to accept, especially for anyone who'd rather not inherit another multi-year-old cacheGroups configuration ever again.

There's another distinction worth calling out separately. Webpack largely forced you to reason about chunking at build time, squinting at a stats.json output and trying to predict what a real visitor's browser cache would actually hold. Turbopack's component-level chunking shifts part of that reasoning to request time, where the runtime can check the actual cache state instead of guessing at it in advance. That's a genuine architectural shift, not merely a renamed configuration flag.

The Catch

This feature is still labeled experimental, and the documentation is upfront about that. One team ran into a case where a dynamically imported charting library got split in a way that briefly triggered a "failed to load chunk" error in production during a deployment, the familiar stale-chunk-reference issue that has affected essentially every bundler with code-splitting support. A hard refresh resolved it for the users who hit it, but it's a useful reminder that finer-grained chunking widens the surface area for that particular failure mode rather than shrinking it.

There's also the matter of tuning these settings without real traffic data, which is essentially guesswork. It's worth resisting the urge to touch the defaults on a fresh project until you have actual navigation analytics showing whether your visitors tend to be one-and-done or the type who hop between a dozen or more routes in a single session. If that data doesn't already exist, this is as good a reason as any to start collecting it before adjusting any chunking configuration. Tuning blind carries a real risk of landing on a worse cache-hit rate than the defaults would have given you, and that kind of regression fails silently. Nobody files a bug report titled "navigation felt a bit slower," they simply stop coming back.

So, Worth Turning On?

For the dashboard scenario, the answer was yes, and the setting stayed enabled. For the marketing site, a single number got adjusted and that was the end of it. None of this repairs a fundamentally broken application, and if your bundle is bloated because of a dependency you didn't actually need, no chunking strategy on earth will bail you out of that mistake. What this feature offers is a focused fix for a narrow problem, which is a refreshing departure from a lot of the build-tooling features shipping this year, a judgment that carries some weight coming from anyone who's burned more evenings than they'd like chasing exactly this class of bug.