What this usually means
When you customize Next.js’ webpack config in next.config.js, small mistakes (like mutating the config in place, misusing webpack APIs, or mismatching loader/webpack versions) cause compilation to break with unhelpful errors. Next.js expects you to extend, not replace, its config objects. Third-party plugin incompatibility, misapplied rules, or missing dependencies are frequent culprits. Many errors reference only the transpiled config or point at internals, making it easy to miss the real root cause.
The first ten minutes — establish facts before touching code.
- 1Reproduce locally: run NODE_OPTIONS='--trace-warnings' next build for detailed stack traces.
- 2Temporarily move your webpack config logic out and restore the original next.config.js — does the error disappear?
- 3Diff next.config.js against a working commit; pay attention to changes in the webpack chain.
- 4Log the config object: add console.dir(config, { depth: 5 }) at the start of your webpack override to see what you're mutating.
- 5Check your package.json for mismatched webpack, loader, or plugin versions (npm ls webpack).
- 6Review node_modules/.bin for missing or conflicting loader binaries.
The specific files, logs, configs, and dashboards that usually own this bug.
- searchnext.config.js, especially the webpack(config, options) override
- searchTerminal output from next build and next dev with --verbose
- searchTrace logs: NODE_OPTIONS='--trace-deprecation --trace-warnings'
- search.next/webpack/logs if using Next.js 13+
- searchpackage.json and lockfiles for dependency mismatches
- searchnode_modules/ to ensure loaders and plugins are installed (e.g. node_modules/file-loader/)
- searchAny custom Babel or PostCSS configs if they interact with webpack
Practical causes, not theory. These are the things you will actually find.
- warningMutating the input config object instead of returning a new object (causes cascading breakage)
- warningUsing incompatible loader/plugin versions with Next.js' bundled webpack
- warningCustom rules that override or disable core Next.js loaders (e.g. breaking CSS/JS handling)
- warningNot handling both client and server targets in the webpack override
- warningEnvironment variable references that don't exist at build time
- warningReturning undefined from the webpack(config, options) hook
Concrete fix directions. Pick the one that matches your root cause.
- buildAlways return a new or extended config: use Object.assign({}, config, { ... })
- buildInstall loaders/plugins using the same major version as Next.js' webpack (see npm ls webpack for version)
- buildMerge rules, don't replace arrays: config.module.rules.push(...) instead of overwriting.
- buildGuard your changes: if (!isServer) { /* client-only logic */ }
- buildUse webpack-merge for complex config extensions to avoid mutation errors
- buildLog your changes and inspect output at each step
A fix you cannot prove is a guess. Close the loop.
- verifiedBuild with next build and confirm zero webpack errors or warnings in output.
- verifiedCheck that assets (images, styles, custom file types) referenced in your app are present in .next/static and render in the UI.
- verifiedSet process.env variables and confirm they render server-side and client-side as intended.
- verifiedRun next dev and confirm hot reload works after editing both JS and CSS files.
- verifiedTest both client and SSR rendering paths for your custom assets/loaders.
- verifiedRun npm audit and ensure no major webpack or loader version mismatches
Things that make this bug worse or harder to find.
- warningDirectly mutating the config object or arrays in place (breaks hot reload, causes non-obvious errors).
- warningBlindly copying webpack config from Stack Overflow or old blogs without checking Next.js version compatibility.
- warningAssuming client/server webpack chains are identical — always check the isServer flag.
- warningIgnoring warning output or suppressing logs too early.
- warningLeaving console.dir or debug logs in production config.
- warningMixing ESM and CommonJS imports in next.config.js
Custom SVG Loader Breaks Production Build in Next.js
Timeline
- 09:30Merged PR adding custom loader for SVGs in next.config.js.
- 09:35CI fails with 'Module parse failed: Unexpected token' for SVG import.
- 09:40Locally, next dev works, but next build fails with 'Invalid configuration object'.
- 09:45Attempted to update svg-inline-loader plugin; error changes to 'Cannot find module svg-inline-loader'.
- 10:00Realize webpack config was mutating module.rules directly, breaking downstream rules.
- 10:15Refactored config to clone and extend rules array; installed missing loader.
- 10:20next build passes; assets verified in .next/static.
I merged a feature branch that added custom webpack rules for SVG imports, aiming to enable both inline and file loading. Our PR review had missed that the config directly pushed to config.module.rules instead of spreading and returning a new array.
The build failed in CI, but dev mode worked — classic symptom of mutation causing subtle breakage. I wasted time updating the loader instead of examining the config shape. Only by logging out the config before and after our override did I spot that subsequent calls to the config hook were seeing mutated arrays.
The fix was to avoid in-place mutation and ensure both the loader plugin and its dependencies matched the Next.js webpack version. This also required a fresh npm install to resolve loader paths. Lesson learned: always treat next.config.js as immutable and test both dev and build flows after webpack changes.
Root cause
Direct mutation of the webpack config's rules array, leading to a broken module.rules structure and missing loaders.
The fix
Refactor the config override to return a new array, install compatible loader versions, and test both next dev and next build to verify.
The lesson
Mutation in next.config.js can break critical Next.js webpack internals; always extend, never overwrite, and validate both development and production output.
Next.js applies its own layers on top of Webpack. When you override webpack(config, options) in next.config.js, that config object is not yours to mutate: it's shared across plugins and features.
Any mutation (especially of arrays like module.rules or plugins) will propagate downstream and can cause silent breakage. Always return a new deep copy if extending rules, or use helpers like webpack-merge. Treat the input config as read-only.
Next.js locks webpack to a specific version. If you install a loader or plugin with a mismatched peerDependency, you'll hit runtime errors such as 'Cannot find module' or cryptic config shape errors.
Run npm ls webpack and npm ls <loader-name> to verify version alignment. If you must use a custom loader, find a version aligned to Next.js' webpack (see node_modules/next/package.json for the bundled webpack version).
Webpack config runs twice: once for client, once for server. Check the isServer flag in the config override to avoid injecting client-side loaders in the server config.
Many bugs only manifest in SSR or in static builds. Always test next build and SSR rendering, not just next dev. Add targeted console logs to confirm which branch is running.
If custom assets (SVG, images, CSS) are missing or malformed in production, it's likely your webpack merge broke core Next.js file handling.
Inspect .next/static and .next/server/chunks for output files post-build. Use webpack-bundle-analyzer for a visual map of what got bundled. If files or code chunks are missing, revisit your merging logic.
Frequently asked questions
Why does my custom loader work in next dev but fail in next build?
Dev and prod builds handle config and asset resolution differently. In prod, your config is more strictly validated, and SSR is involved. Mutations or missing dependencies often only break on next build.
Is it safe to override module.rules in next.config.js?
Never fully replace module.rules; always merge or append. Next.js’ core rules handle JS, CSS, and images. Overwriting will break built-in asset support.
How can I debug which webpack config Next.js is actually using?
Insert console.dir(config, { depth: 5 }) in your webpack override. For deeper inspection, use process.env.WEBPACK_BUNDLE_ANALYZER and compare dev/prod outputs.
Do I need to match my custom loader's version to Next.js' webpack?
Yes. Loader and plugin versions must be compatible with the major webpack version Next.js uses. Mismatched versions will crash or exhibit unpredictable errors.
What's the cleanest way to add a new loader?
Use config.module.rules.push({...}), never overwrite. If adding MIME handling, position your rule before the default file-loader (see config.module.rules.findIndex for insertion).