Turbopack in Next.js 16.3 lands three build-related changes that matter in practice: development memory eviction (on by default), a persistent file cache for builds, and an experimental Rust port of the React Compiler. This post focuses on the two that compound as an app grows — the Rust React Compiler and the persistent build cache — covering how they work and how to enable them.
The problem with the React Compiler until now
The React Compiler inserts memoization automatically at compile time, so you get the effect of useMemo and useCallback without writing them by hand. It optimizes re-renders without cluttering your components, which is why adoption has grown.
The catch: until now the React Compiler shipped only as a Babel transform. On large apps that became a bottleneck. Babel transforms consume JavaScript execution resources, so the more code you have, the more the compiler slows the build. Turbopack could build fast in Rust, only to wait on the React Compiler's JS execution — the fast part held back by the slow part.
The Rust React Compiler
The React team published a native Rust port of the compiler, and Vercel integrated it into Turbopack. The React Compiler's work now runs Rust-native inside Turbopack's build pipeline. The contention over JS execution resources disappears, so the larger the app, the bigger the win.
In Vercel's early tests, large React apps like v0 saw compilation-time reductions of 20–50% (source: the Next.js blog). The effect scales with app size and component count, so small apps see little difference while large ones benefit most.
In 16.3 this is an experimental feature, so you enable it explicitly in next.config.ts:
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
experimental: {
reactCompiler: true,
},
};
export default nextConfig;If you want to adopt the React Compiler gradually, scoping it to specific directories is the realistic path. Enable it first on a low-blast-radius feature, confirm rendering behavior is unchanged, then widen. Because the compiler inserts memoization automatically, code that depends on side effects or on referential identity can behave differently. Verifying against your existing tests for regressions is a prerequisite before rolling it out.
The persistent build cache
The other practical change is a persistent file cache for builds, enabled with the turbopackFileSystemCacheForBuild flag:
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
experimental: {
turbopackFileSystemCacheForBuild: true,
},
};
export default nextConfig;With this on, the generated .next directory can be reused between runs. It helps local back-to-back builds, but the real payoff is in CI. Most CI providers have a cache step, and adding .next to it lets you skip recompiling the parts that did not change.
On GitHub Actions, for instance, you cache the build output like this:
- name: Cache Next.js build
uses: actions/cache@v4
with:
path: |
.next/cache
key: nextjs-${{ hashFiles('**/package-lock.json') }}-${{ github.sha }}
restore-keys: |
nextjs-${{ hashFiles('**/package-lock.json') }}-For teams that merge many small PRs a day, this compounds. If 95% of the app did not change, you stop paying the cold-compile tax on that 95% every time. On a pipeline that used to full-build on every merge, the difference is noticeable.
Development memory eviction
The third change: the dev server's memory eviction is now on by default. Building on the filesystem persistence introduced in 16.1, Turbopack can offload in-memory cache results to disk and remove them from memory. This mitigates the unbounded memory growth of long dev sessions.
It requires no configuration and is on by default, so you get it just by upgrading to 16.3. If your workflow leaves next dev running for hours, you should feel the memory usage plateau.
Decide the cache policy first
The thing to settle before adopting these features is your cache policy. The persistent build cache is powerful, but a bad cache-key design leads to the classic failure: pulling a stale cache so changes do not show up.
At webhani we recommend this order:
- Enable
turbopackFileSystemCacheForBuildlocally first, and confirm on back-to-back builds that caching behaves as expected. - Always include a dependency hash (such as
package-lock.json) in the CI cache key. Design it so the cache rebuilds when dependencies change. - Enable the Rust React Compiler gradually, starting with projects that have solid regression tests.
The Rust React Compiler is a "make it faster" feature; the persistent build cache is a "decide the operation first, then use it" feature. Reverse the order and the cache you added for speed becomes the cause of a debugging session. Understand the cache behavior and lock down the key design before putting it into CI.
Wrapping up
The build-side changes in Next.js 16.3 map directly onto large-app reality. The Rust React Compiler removes the wait on JS execution, and the reduction grows with component count. The persistent build cache pays off for teams merging small PRs frequently. Development memory eviction is on by default, so you benefit just by upgrading. As for rollout order: pin the cache policy first, and enable the React Compiler gradually where regression tests are in place.