A Next.js app's `next.config.ts` has a custom `webpack()` function that adds an SVGR rule so `.svg` files import as React components. After the team switches to Turbopack, the rule stops applying. Why, and how do you express that rule for Turbopack?
answer
- webpack() is webpack-only, never read
- own config key: turbopack
- glob to loaders, plus an as field
- loaders subset, plugins not at all
- keep both blocks while migrating
basics
~20 sThe webpack() function in next.config is only read by webpack, so Turbopack ignores it entirely and Next warns rather than silently honouring it. Port the rule to the turbopack.rules key, which maps a file glob to a list of loaders — a supported subset of webpack loaders, with no plugin equivalent.
solid answer
~50 s`webpack(config)` is a hook into the webpack build; Turbopack is a different bundler with its own configuration surface, so nothing in that function reaches it. Next surfaces a warning when it sees a custom webpack config while bundling with Turbopack, which is the signal to port rather than a hint that it half-works. The Turbopack equivalent lives under the top-level `turbopack` key in `next.config`: `rules` maps a glob such as `'*.svg'` to `{ loaders: ['@svgr/webpack'], as: '*.js' }`, `resolveAlias` covers path aliases, and `resolveExtensions` covers extension resolution. Turbopack supports a **subset** of webpack loaders — those that stay within the loader API it implements — and has **no plugin API at all**, so anything shipped as a webpack plugin needs a native equivalent or a different place in the pipeline. Keeping both blocks in the file is fine during a migration, since the `webpack()` function still applies whenever you run with `--webpack`.
code
typescript · 24 linesimport type { NextConfig } from 'next'
const nextConfig: NextConfig = {
// Read only when Turbopack is the bundler
turbopack: {
rules: {
'*.svg': { loaders: ['@svgr/webpack'], as: '*.js' },
},
resolveAlias: {
'@ui': './src/components/ui',
},
},
// Read only when webpack is the bundler (next dev --webpack)
webpack(config) {
config.module.rules.push({
test: /\.svg$/,
use: ['@svgr/webpack'],
})
return config
},
}
export default nextConfiggo deeper
Know that Turbopack does not read the webpack() function in next.config, and that Turbopack's own settings live under a turbopack key — so a bundler switch means porting configuration, not inheriting it.
Be able to write the port: a glob mapped to loaders with an as field under turbopack.rules, aliases under resolveAlias, and the awareness that loaders are a supported subset while plugins have no equivalent.
Show the migration discipline — audit next.config and the toolchain first, keep both config blocks so --webpack still works, port and verify one rule at a time, and treat the plugin wall as a scheduling decision rather than a surprise.
Frame bundler customization as a liability to budget. Every custom rule and plugin is a migration cost the org pays again on the next toolchain move, so set expectations for when a build-time hack is worth taking on at all.
## Why the rule silently stops applying `next.config` exposes a `webpack(config, context)` function whose whole purpose is to hand you webpack's configuration object so you can mutate it. That contract is bundler-specific by construction: the object is a webpack config, the rules you push are webpack `module.rules`, and the only thing that ever reads them is webpack. Turbopack does not consume that object. It is not a webpack-compatible bundler with a translation layer over the config — it is a separate implementation with its own configuration. So the SVGR rule is not misapplied or partially applied; it is simply never seen. Next detects the situation and warns that a custom webpack configuration is present while Turbopack is bundling, which is worth reading rather than scrolling past. ## The Turbopack configuration surface Turbopack's options live under a top-level `turbopack` key in `next.config` (it was previously nested under `experimental.turbo` before being promoted). The three you reach for most: - **`rules`** — an object keyed by glob, each value naming the loaders to run and what the result should be treated as. This is the port target for webpack `module.rules` entries. - **`resolveAlias`** — path aliases, the equivalent of webpack's `resolve.alias`. - **`resolveExtensions`** — the extension list tried during resolution. For the SVGR case: ```ts turbopack: { rules: { '*.svg': { loaders: ['@svgr/webpack'], as: '*.js' }, }, } ``` The `as` field tells Turbopack what the loader's output should be treated as — here, a JavaScript module, because SVGR turns the SVG into a React component. ## Loaders: a subset, not the ecosystem Turbopack implements the webpack loader interface well enough that many loaders run unmodified, and the popular ones generally do. But it is a subset. A loader that reaches into webpack internals — compilation objects, plugin hooks, webpack-specific APIs on the loader context — has nothing to reach for and will fail. When evaluating a migration, the question to ask about each loader is not "is it a webpack loader" but "does it stay inside the loader API." Before reaching for a loader at all, check whether you need one. Turbopack handles CSS, CSS Modules, PostCSS and Sass natively, plus the transforms Next already owns through SWC, so a good share of legacy `webpack()` blocks turn out to be re-implementing something now built in. ## Plugins: no equivalent exists This is the hard wall and the one worth naming explicitly in an interview. webpack's plugin API lets code hook arbitrary points of the compilation lifecycle, and a great deal of tooling is built on it — bundle manipulation, artifact generation, source-map upload to error trackers, custom asset emission. **Turbopack exposes no such API.** A webpack plugin cannot be ported by rewriting configuration; it needs either a Turbopack-native integration published by that tool, or the work moved somewhere else — a separate CLI step in CI, for instance. One more difference that catches teams: the webpack path in Next auto-detected a Babel config file and switched the transform pipeline to Babel. Turbopack does not do that. Custom Babel plugins are not picked up by their config file alone. ## Running the migration Keep both configuration blocks in `next.config` while you migrate. The `webpack()` function still applies whenever the bundler is webpack, so `--webpack` remains a working fallback, and `turbopack.rules` applies whenever Turbopack is bundling. Port one rule at a time, verify the imports it governs actually behave — an SVG rendering as a component rather than a URL string, for example — and delete the webpack block only once nothing runs on webpack any more.
- Your build depends on a webpack plugin, not a loader. What are your options under Turbopack?Porting the configuration will not help — Turbopack has no plugin API, so there is nothing to hook. The realistic options are a Turbopack-native integration published by that tool, moving the work out of the bundler into a separate CI step, dropping the capability, or staying on `--webpack` for builds until an equivalent exists. Treat the last one as temporary and revisit it each Next release.
- Which webpack loaders are likely to work under Turbopack, and which are not?Loaders that stay inside the loader API — read source in, return transformed source out, using the standard loader context — generally run unmodified, which covers most popular ones. Loaders that reach into webpack internals such as the compilation object or plugin hooks have nothing to bind to and will fail. Check the loader's implementation rather than assuming, and confirm the transform actually applied.
- Before porting a webpack rule at all, what should you check?Whether Turbopack already handles it. CSS, CSS Modules, PostCSS and Sass are built in, and TypeScript and JSX go through SWC as part of Next itself. A lot of `webpack()` blocks in older projects predate those built-ins and re-implement them, so the cleanest migration for those entries is deletion rather than translation.
saying these in an interview costs you the question
- Believes Turbopack reads and translates the existing webpack() config
- Thinks any webpack plugin works if you list it under turbopack
- Assumes every webpack loader runs unchanged under Turbopack
- Says a Babel config file is picked up automatically like it was with webpack
- Deletes the webpack() block before the team has stopped using --webpack