Vital: This text describes options which are partially out there in Subsequent.js 15 canary and partially proposed for a future Subsequent.js launch. As of writing, Subsequent.js 16 has not been launched. Confirm every characteristic’s stability standing and availability earlier than making use of any adjustments to manufacturing. Instructions, config shapes, and API surfaces described right here needs to be confirmed towards the official Subsequent.js changelog and documentation on your put in model.
Three adjustments form how Subsequent.js 15+ initiatives construct, render, and cache: Turbopack shifting towards changing into the default manufacturing bundler, the React Compiler progressing from experimental to built-in for computerized memoization, and a restructured caching layer constructed on express APIs. Every touches construct efficiency, runtime conduct, and the improve path for each present Subsequent.js undertaking.
Desk of Contents
Turbopack because the Default Bundler
What Modified in Subsequent.js 15 and What Is Coming
Subsequent.js 15 ships Turbopack for the event server by way of the --turbopack flag and is stabilizing it for manufacturing builds. Webpack stays the default manufacturing bundler. A future Subsequent.js launch will make Turbopack the default for each growth and manufacturing builds. Webpack will keep out there as a fallback, however Vercel is shifting major growth funding towards Turbopack. New initiatives created with create-next-app in Subsequent.js 15 immediate the person to decide into Turbopack; a future model will seemingly allow it by default.
Migration Guidelines: Transferring from Webpack to Turbopack
The transition from webpack to Turbopack requires systematic auditing. The next guidelines covers the important migration steps:
- Audit
subsequent.config.jsfor customized webpack configurations. Anywebpack()operate overrides is not going to carry over to Turbopack robotically. - Establish unsupported webpack plugins and exchange
webpack()operate overrides with Turbopack loader configuration. Not each webpack plugin has a direct counterpart in Turbopack’s loader system. Turbopack makes use of aguidelinesobject as an alternative of webpack’s configuration object handed by reference; translate every plugin and override individually. - Replace
.babelrcor Babel configuration. Turbopack makes use of SWC for transpilation; Babel isn’t supported. Customized Babel plugins want SWC equivalents or should be eliminated. - Confirm CSS module and PostCSS compatibility. Turbopack helps CSS modules and PostCSS, however edge circumstances in customized PostCSS plugin chains needs to be examined.
- Take a look at third-party packages that hook into webpack internals for code technology or asset dealing with. These usually break silently below Turbopack.
- Run
subsequent constructand seize the complete output for assessment. Pipe the output to a log file to floor compatibility points which may not trigger laborious failures however have an effect on output. (See improve sequence beneath for the precise command.) - Benchmark construct instances earlier than and after migration. Doc a baseline with webpack so enhancements (or regressions) are measurable.
The most typical migration activity includes translating customized webpack loaders and aliases into Turbopack’s configuration format. Here’s a before-and-after comparability of a subsequent.config.js that provides an SVG loader and path aliasing:
const nextConfig = {
webpack(config) {
config.module.guidelines.push({
check: /.svg$/,
use: ['@svgr/webpack'],
});
config.resolve.alias = {
...config.resolve.alias,
'@parts': './src/parts',
'@lib': './src/lib',
};
return config;
},
};
module.exports = nextConfig;
const path = require('path');
const nextConfig = {
experimental: {
turbo: {
guidelines: {
'*.svg': {
loaders: ['@svgr/webpack'],
as: '*.js',
},
},
resolveAlias: {
'@parts': path.resolve(__dirname, 'src/parts'),
'@lib': path.resolve(__dirname, 'src/lib'),
},
},
},
};
module.exports = nextConfig;
In case your undertaking makes use of ESM ("sort": "module" in bundle.json), __dirname isn’t out there. Use this manner as an alternative:
import { fileURLToPath } from 'url';
import { dirname, resolve } from 'path';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const nextConfig = {
experimental: {
turbo: {
guidelines: {
'*.svg': {
loaders: ['@svgr/webpack'],
as: '*.js',
},
},
resolveAlias: {
'@parts': resolve(__dirname, 'src/parts'),
'@lib': resolve(__dirname, 'src/lib'),
},
},
},
};
export default nextConfig;
Observe: Confirm that @svgr/webpack works below Turbopack’s loader compatibility layer earlier than counting on it. As of Subsequent.js 15, some webpack loaders are unsupported. Seek the advice of the Turbopack documentation for the supported loader record. Use path.resolve(__dirname, '...') for dependable alias decision reasonably than relative string paths.
Loaders are declared below guidelines with glob patterns as keys, and the as property tells Turbopack deal with the output. Path aliases transfer into resolveAlias as a flat object.
Falling Again to Webpack (When and How)
For initiatives with deep webpack plugin dependencies or monorepo tooling that has not but been up to date for Turbopack, persevering with to make use of webpack is simple. In Subsequent.js 15, webpack is the default manufacturing bundler. To make use of Turbopack for growth solely, add --turbopack to your subsequent dev command. No config secret’s wanted to revive webpack; merely omit the --turbopack flag.
Acceptable use circumstances for staying on webpack embody legacy plugin dependencies that don’t have any SWC or Turbopack equal, monorepo setups with customized webpack federation configurations, and initiatives utilizing Babel plugins with no SWC different. Monitor Turbopack’s compatibility progress by way of the Turbopack roadmap and concern tracker.
Efficiency Benchmarks at a Look
The next desk exhibits hypothetical figures for a mid-size Subsequent.js undertaking (~200 routes, ~150 dependencies). These are usually not cited benchmarks:
| Metric | Webpack | Turbopack |
|---|---|---|
| Chilly manufacturing construct | ~120s | ~45s |
| Incremental rebuild (manufacturing) | ~18s | ~4s |
| Dev server startup | ~8s | ~1.2s |
These numbers are illustrative, not measured towards a selected public benchmark. For cited benchmarks with methodology, see the Turbopack benchmark web page on turbo.construct. Actual-world outcomes differ relying on undertaking measurement, the variety of dependencies, and the complexity of customized configurations. Groups ought to run their very own benchmarks utilizing the baseline documentation step from the migration guidelines to validate enhancements of their particular context.
The React Compiler: From Experimental Towards Default
What the React Compiler Does
The React Compiler performs computerized memoization of parts and hooks at construct time. It analyzes part purity and dependency graphs to find out which values may be safely memoized. When the compiler can not show purity, it leaves the part unoptimized. For parts it will probably confirm, it inserts the equal of useMemo, useCallback, and React.memo calls into the compiled output, eliminating pointless re-renders with out requiring builders to manually wrap values and callbacks. The compiler operates on the precept that React parts needs to be pure features of their props and state. When it verifies {that a} part or expression meets this contract, it applies granular memoization that may be tedious and error-prone to take care of by hand.
The React Compiler performs computerized memoization of parts and hooks at construct time. It analyzes part purity and dependency graphs to find out which values may be safely memoized. When the compiler can not show purity, it leaves the part unoptimized.
Enabling and Configuring the React Compiler
As of Subsequent.js 15, the React Compiler is offered as an experimental opt-in characteristic. A future launch will allow it by default, based mostly on the present RFC trajectory. The React Compiler requires React 19 (or React 17+ with the react-compiler-runtime shim). In Subsequent.js 15, the documented configuration is:
const nextConfig = {
experimental: {
reactCompiler: true,
},
};
module.exports = nextConfig;
Observe: The precise config form for excluding particular paths (e.g., an exclude array) needs to be verified towards the React Compiler documentation and your put in Subsequent.js model’s config schema. The form might differ from what’s proven in neighborhood examples.
To disable the compiler, take away the reactCompiler key or set it to false. For particular person parts or features that have to decide out, the "use no memo" directive may be positioned on the high of a file or on the high of a selected operate physique:
"use no memo";
import { useEffect, useRef } from 'react';
export default operate IdentitySensitive({ onDataPoint }) {
const chartRef = useRef(null);
useEffect(() => {
chartRef.present?.bindCallback(onDataPoint);
}, [onDataPoint]);
return <div ref={chartRef} />;
}
The "use no memo" directive tells the compiler to skip computerized memoization for all the file (when positioned on the file degree) or for a selected operate (when positioned on the high of a operate physique). That is applicable when a part is determined by intentional referential id conduct that the compiler’s memoization would break.
Refactoring Current Code: What to Take away, What to Preserve
With the React Compiler energetic, handbook useMemo, useCallback, and React.memo wrappers will usually change into redundant. Confirm utilizing the compiler’s annotation output earlier than eradicating them to keep away from efficiency regressions. The compiler handles these optimizations robotically and sometimes extra granularly than hand-written memoization, however blanket removing with out verification isn’t really helpful.
Doubtlessly protected to take away (after verification): useMemo wrapping derived values from props or state, useCallback round occasion handlers handed to youngster parts, and React.memo on parts which are already pure.
Preserve: Memoization tied to costly non-rendering computation, similar to heavy knowledge transformations inside occasion handlers or results that aren’t a part of the render path. The compiler optimizes rendering; it doesn’t optimize arbitrary JavaScript computation.
Here’s a before-and-after comparability:
import { useMemo, useCallback, memo } from 'react';
const ProductCard = memo(operate ProductCard({ product, onAddToCart }) {
const discountedPrice = useMemo(
() => product.value * (1 - product.low cost),
[product.price, product.discount]
);
const handleClick = useCallback(() => {
onAddToCart(product.id, discountedPrice);
}, [onAddToCart, product.id, discountedPrice]);
return (
<div>
<h3>{product.identify}</h3>
<p>${discountedPrice.toFixed(2)}</p>
<button onClick={handleClick}>Add to Cart</button>
</div>
);
});
export default ProductCard;
export default operate ProductCard({ product, onAddToCart }) {
const discountedPrice = product.value * (1 - product.low cost);
const handleClick = () => {
onAddToCart(product.id, discountedPrice);
};
return (
<div>
<h3>{product.identify}</h3>
<p>${discountedPrice.toFixed(2)}</p>
<button onClick={handleClick}>Add to Cart</button>
</div>
);
}
The eslint-plugin-react-compiler bundle may be added to a undertaking’s ESLint configuration to flag violations of the Guidelines of React that may forestall the compiler from optimizing successfully.
The New Cache API
Why the Outdated Caching Mannequin Is Being Changed
Subsequent.js 14 and early 15 releases aggressively cached fetch() outcomes and route knowledge by default. This led to widespread developer confusion and off knowledge bugs, significantly in dynamic functions the place knowledge freshness was important. The implicit caching conduct meant that builders usually couldn’t predict whether or not a web page was serving recent or cached content material with out deep data of the framework’s inner caching layers. The brand new route shifts to an opt-in caching mannequin the place builders should explicitly declare caching conduct utilizing semantic APIs.
The implicit caching conduct meant that builders usually couldn’t predict whether or not a web page was serving recent or cached content material with out deep data of the framework’s inner caching layers. The brand new route shifts to an opt-in caching mannequin the place builders should explicitly declare caching conduct utilizing semantic APIs.
The “use cache” Directive and cacheLife / cacheTag APIs
Prerequisite: These APIs are experimental in Subsequent.js 15. To allow them, add experimental: { dynamicIO: true } to your subsequent.config.js. Affirm their stability standing in your goal model’s launch notes earlier than manufacturing use.
The brand new caching system facilities on three primitives: the "use cache" directive, the cacheLife() operate, and the cacheTag() operate.
The "use cache" directive may be positioned on the operate or file degree to point that the output needs to be cached. cacheLife() defines how lengthy cached entries stay legitimate, accepting preset profiles like "minutes", "hours", or customized period objects (see the canary documentation for the complete record of legitimate presets and the customized object form, which can require { stale, revalidate, expire } reasonably than { revalidate } alone). cacheTag() attaches a string tag to a cached entry, enabling focused invalidation by way of revalidateTag().
import { cacheLife, cacheTag } from 'subsequent/cache';
export default async operate ProductsPage() {
'use cache';
cacheLife("hours");
cacheTag("merchandise");
const res = await fetch('https://api.instance.com/merchandise');
if (!res.okay) {
throw new Error(`Merchandise API error: ${res.standing} ${res.statusText}`);
}
let merchandise;
attempt {
merchandise = await res.json();
} catch {
throw new Error('Merchandise API returned malformed JSON');
}
if (!Array.isArray(merchandise)) {
throw new Error(`Anticipated array from merchandise API, bought ${typeof merchandise}`);
}
return (
<ul>
{merchandise.map((p) => (
<li key={p.id}>
{p.identify} — ${Quantity(p.value).toFixed(2)}
</li>
))}
</ul>
);
}
import { revalidateTag } from 'subsequent/cache';
import { timingSafeEqual } from 'crypto';
operate safeCompare(a, b) {
const bufA = Buffer.from(a ?? '', 'utf8');
const bufB = Buffer.from(b ?? '', 'utf8');
if (bufA.size !== bufB.size) return false;
return timingSafeEqual(bufA, bufB);
}
export async operate POST(request) {
const secret = course of.env.REVALIDATE_SECRET;
if (!secret) {
console.error('REVALIDATE_SECRET atmosphere variable isn't set.');
return new Response('Service misconfigured', { standing: 500 });
}
const offered = request.headers.get('x-revalidate-secret') ?? '';
if (!safeCompare(offered, secret)) {
return new Response('Unauthorized', { standing: 401 });
}
attempt {
await Promise.resolve(revalidateTag('merchandise'));
return Response.json({ revalidated: true });
} catch (err) {
console.error('revalidateTag failed:', err);
return new Response('Revalidation failed', { standing: 500 });
}
}
Safety word: By no means expose revalidation endpoints with out authentication. The instance above requires an x-revalidate-secret header matching the REVALIDATE_SECRET atmosphere variable, in contrast utilizing a timing-safe equality test to forestall secret enumeration. If REVALIDATE_SECRET isn’t set, the endpoint returns 500 to fail closed. With out these guards, any caller can invalidate your cache, enabling denial-of-service or forcing costly recomputations.
When the /api/revalidate endpoint is named with the right secret, all cached entries tagged with "merchandise" are invalidated, and the subsequent request triggers a recent computation.
Migrating from fetch Cache Choices and revalidate Config
The mapping from present caching patterns to the brand new cache API equivalents is direct:
| Present Sample | New Equal |
|---|---|
fetch(url, { cache: 'force-cache' }) | "use cache" directive with cacheLife() at desired period |
fetch(url, { cache: 'no-store' }) | No "use cache" directive (default is uncached) |
fetch(url, { subsequent: { revalidate: 60 } }) | "use cache" with cacheLife({ stale: 0, revalidate: 60, expire: 3600 }) (confirm actual form towards docs) |
export const revalidate = 60 (route section) | cacheLife({ stale: 0, revalidate: 60, expire: 3600 }) contained in the operate physique (confirm actual form towards docs) |
unstable_cache(fn, keys, opts) | "use cache" directive on the operate (word: "use cache" is itself experimental in Subsequent.js 15; affirm stability earlier than treating this as a stability improve) |
fetch(url, { subsequent: { tags: ['x'] } }) | cacheTag('x') inside a "use cache" operate |
Caching not belongs to the fetch name or route section config. It belongs to the operate that performs the work. This makes caching conduct seen and auditable on the operate degree reasonably than scattered throughout fetch choices and file-level exports.
Placing It All Collectively: Upgrading a Venture Step by Step
Conditions
- Node.js: ≥18.18.0 (required for Subsequent.js 15)
- Package deal supervisor: The examples beneath use
npm. Regulate forpnpmoryarnas wanted. - React: React 19 and React DOM 19 are anticipated for the React Compiler integration. Affirm the required React model within the launch notes on your goal Subsequent.js model.
Really helpful Improve Sequence
Carry out the improve incrementally, addressing every pillar in sequence:
- Replace
subsequentandreactpackages to the goal variations. - Run the automated codemod to use protected transformations.
- Deal with Turbopack compatibility utilizing the migration guidelines.
- Audit and simplify memoization with the React Compiler. That is additionally an excellent time so as to add
eslint-plugin-react-compilerand catch purity violations earlier than they silently forestall optimizations. - Migrate the caching technique to the brand new semantic APIs. Begin with a single high-traffic path to validate conduct earlier than changing the remainder.
- Run the complete check suite and evaluate construct output sizes and efficiency towards the documented baseline.
npm set up subsequent@newest react@19 react-dom@19
npx @subsequent/codemod improve
npx subsequent construct 2>&1 | tee construct.log
npm check
npx subsequent construct
The codemod handles mechanical transformations similar to updating import paths and deprecated API signatures. It doesn’t deal with customized webpack-to-Turbopack migration, memoization removing, or cache API migration, all of which require handbook assessment.
Key Takeaways
The Subsequent.js trajectory from model 15 onward delivers sooner builds by means of Turbopack, much less handbook memoization boilerplate by means of the React Compiler, and predictable caching by means of the brand new "use cache" API with cacheLife and cacheTag. Every pillar features a fallback: webpack stays out there by omitting the --turbopack flag (or by remaining on the present default), the React Compiler may be disabled per-file, per-function, or project-wide, and the cache API is opt-in by design.
Migration is incremental. Groups don’t want to deal with all three areas concurrently. The migration guidelines above can double as a project-level monitoring device for groups planning their improve.
The Subsequent.js changelog and the improve documentation have the version-specific particulars.
Supply hyperlink


