How to Fix Common Next.js Build Errors: Troubleshooting Guide
On this page
How to Fix Common Next.js Build Errors: Troubleshooting Guide
I've been building Next.js apps for years now, and if there's one universal experience among Next.js developers, it's that sinking feeling when next build spits out a wall of red text. Your app works perfectly in dev mode, you're feeling confident, you run the build, and... something explodes.
The thing is, most Next.js build errors fall into a pretty small set of categories. Once you've seen each one a few times, they stop being scary and start being just another thing to fix. Let me walk you through the ones I run into most often and how to actually solve them.
Module Not Found Errors
This is probably the single most common build error, and honestly, it's usually the easiest to fix. You'll see something like:
Module not found: Can't resolve 'some-package'
Why it happens: Nine times out of ten, you either forgot to install the package, there's a typo in your import, or the package is in devDependencies when it needs to be in dependencies.
How to fix it:
- Run
npm install some-packageoryarn add some-packageto make sure the dependency is actually installed. - Double-check your import statement. JavaScript imports are case-sensitive, and I've spent an embarrassing amount of time debugging a build error caused by
import ... from 'React-Query'instead of'react-query'. - If the package is a devDependency but you're importing it in production code, move it to
dependenciesin yourpackage.json. - When in doubt, nuke everything and start fresh:
rm -rf node_modules package-lock.json && npm install.
For relative imports, make sure the file path actually matches what exists on disk. And if you're using TypeScript path aliases in tsconfig.json, make sure they're also configured in your next.config.js or next.config.mjs — this trips people up constantly.
"document is not defined" and "window is not defined"
Ah, the classic SSR gotcha. Next.js renders your pages on the server first, and the server doesn't have window, document, or localStorage. If your code tries to access any of these during the server render, boom.
How to fix it:
- Wrap browser-only code in a check:
if (typeof window !== 'undefined') {
// safe to use window or document here
}
- Use
useEffect, which only runs on the client:
import { useEffect } from 'react';
useEffect(() => {
const width = window.innerWidth;
// client-side logic here
}, []);
- For third-party libraries that touch the DOM the moment they're imported (looking at you, Leaflet), use dynamic imports with SSR disabled:
import dynamic from 'next/dynamic';
const MapComponent = dynamic(() => import('../components/Map'), {
ssr: false,
});
I've lost count of how many times I've hit this with chart libraries and map components. If a library's README doesn't mention SSR compatibility, assume it doesn't have it.
Hydration Mismatch Errors
These are the sneaky ones. The error message looks like:
Hydration failed because the initial UI does not match what was rendered on the server.
What's actually going on: React rendered HTML on the server, then tried to "hydrate" it on the client, and the two didn't match. React gets very upset about this.
Common culprits:
- Rendering something based on
Date.now(),Math.random(), or any value that differs between server and client. - Using browser APIs during the initial render (before
useEffectruns). - Invalid HTML nesting — like putting a
<div>inside a<p>tag. This one drove me nuts for hours once. - Browser extensions injecting DOM elements. Seriously, test in incognito mode if you're stumped.
How to fix it:
- Move anything that depends on client-specific data into
useEffect. - Use
suppressHydrationWarningfor things like timestamps where a small mismatch is acceptable. - Validate your HTML nesting. Some combinations that "work" in browsers are technically invalid and will cause hydration issues.
- Test in an incognito window to rule out browser extensions.
Image Optimization Errors
Next.js's <Image> component is great once it's working, but it's picky about configuration. Common errors:
Invalid src prop— you're loading an image from a domain that isn't allowlisted.Image with src has either width or height modified— missing required dimensions.
How to fix it:
Add external image domains to your next.config.js:
/** @type {import('next').NextConfig} */
const nextConfig = {
images: {
remotePatterns: [
{
protocol: 'https',
hostname: 'example.com',
},
],
},
};
module.exports = nextConfig;
Always provide width and height props for static images, or use the fill prop with a positioned parent container. If image optimization is giving you grief in deployment and you don't actually need it, you can disable it with unoptimized: true in the images config. Not ideal, but sometimes you just need to ship.
TypeScript and ESLint Errors Blocking the Build
By default, Next.js runs both TypeScript type checking and ESLint during the build. Any error fails the build — no exceptions.
How to fix it:
- Actually fix the errors. The build output tells you exactly which file and line number. Most of the time this is the right call.
- If you need to unblock a deployment urgently, you can temporarily bypass these checks:
const nextConfig = {
typescript: {
ignoreBuildErrors: true,
},
eslint: {
ignoreDuringBuilds: true,
},
};
I want to be really clear though: this is a temporary escape hatch, not a permanent solution. I've seen teams turn this on "just for now" and leave it on for months. Then they wonder why they have weird runtime crashes that would've been caught by TypeScript. Fix the actual errors when you can, and add targeted // @ts-ignore comments for the rare cases where you genuinely need to suppress something.
Out of Memory Errors During Build
If you're working on a larger Next.js project, you might hit this:
FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memory
How to fix it:
- Increase the Node.js memory limit:
NODE_OPTIONS="--max-old-space-size=8192" next build
- Code-split your larger pages and use dynamic imports for heavy components. If you're importing a massive charting library on every page, that's probably your problem.
- Install
@next/bundle-analyzerand see what's actually taking up space. I've found some surprising culprits this way — one time it was a localization library pulling in dictionaries for 100+ languages when we only needed English. - If you're generating tons of static pages, consider Incremental Static Regeneration (ISR) instead of building them all upfront.
Environment Variable Issues
Next.js has a specific way of handling env vars, and it trips up almost everyone at some point.
The rules:
- Variables in
.env.localare only available on the server. - To expose a variable to the browser, prefix it with
NEXT_PUBLIC_. - Environment variables are baked in at build time, not runtime. If you change your
.envfile after building, you need to rebuild.
The classic mistake: "It works in dev but the variable is undefined in production."
How to fix it:
- If the variable is used in client-side code, it needs the
NEXT_PUBLIC_prefix. No prefix, no browser access. Full stop. - Make sure your deployment platform (Vercel, AWS, whatever) has the env vars configured in its settings — not just in your local
.envfile. - Restart your dev server after changing
.envfiles. They're only read at startup, and I've definitely wasted 20 minutes debugging an env var that "wasn't working" because I forgot to restart.
API Route and Server Component Errors
With the App Router, Server Components are the default, and they have strict rules. The most common error is trying to use useState or useEffect in a Server Component.
How to fix it:
- Add
'use client'at the top of any component that uses React hooks, browser APIs, or event handlers. - Keep Server Components as the default and only opt into client rendering when you actually need it.
- For API routes in the App Router (
app/api/route.js), export named HTTP method functions, not a default export:
// app/api/hello/route.js
export async function GET(request) {
return Response.json({ message: 'Hello' });
}
Build Failures After Upgrading Next.js
Every major Next.js upgrade breaks something. It's just the nature of a framework that moves this fast. I've learned to never upgrade Next.js in the middle of a sprint.
How to handle it:
- Always read the official migration guide for the version you're upgrading to. Next.js actually maintains pretty detailed upgrade docs.
- Use the codemod tool to automate common changes:
npx @next/codemod@latest upgrade. - Upgrade one major version at a time. Jumping from 13 to 15 in one shot is asking for trouble — go 13 to 14, fix everything, then 14 to 15.
- Watch out for deprecated APIs.
getStaticPropsandgetServerSidePropsstill work in the Pages Router but the App Router uses completely different patterns.
Static Generation Errors with Dynamic Data
When using generateStaticParams (App Router) or getStaticPaths (Pages Router), your build can fail if the data source is unavailable or returns something unexpected.
How to fix it:
- Make sure your API or database is accessible from wherever you're building. This sounds obvious, but I've seen builds fail because the CI server couldn't reach a database that was only accessible from the office VPN.
- Add error handling to your data-fetching functions. One failed API request shouldn't bring down the entire build.
- Use
dynamicParams: true(App Router) orfallback: 'blocking'(Pages Router) to handle pages that weren't generated at build time.
Practical Tips for Debugging Any Build Error
When you hit something that doesn't fit the categories above, here's my general approach:
- Read the full error message. I know this sounds patronizing, but Next.js error messages have gotten genuinely good. They often include links to docs that explain exactly what's wrong.
- Reproduce locally. Run
next buildon your machine before pushing to CI/CD. So many build errors are environment-specific. - Search the Next.js GitHub issues. Whatever you're hitting, someone else has probably hit it too.
- Isolate the problem. If the error doesn't point to a specific file, start removing recently changed code until the build passes, then add things back one at a time.
- Clear all caches. You'd be amazed how many "impossible" build errors go away when you do this:
rm -rf .next node_modules package-lock.json
npm install
npm run build
Stale caches cause an unbelievable number of phantom build failures. Whenever I'm truly stuck, this is the first thing I try.
Frequently Asked Questions
Q: Why does my Next.js app work in development but fail during build?
Dev mode is way more forgiving than the production build. The build process does static analysis, type checking, and actually pre-renders pages — all of which can surface errors that the dev server happily ignores. Always test with next build before deploying. I have this burned into my workflow now.
Q: How do I fix "You're importing a component that needs useState" errors?
You're using a React hook inside a Server Component. Add 'use client' at the top of the file, or move the stateful logic into a separate client component. The second approach is usually better because it keeps most of your component tree on the server.
Q: Can I skip the build step and deploy my Next.js app in development mode?
Technically you can, but please don't. Dev mode is unoptimized, leaks debug info, and is significantly slower. Always deploy a production build.
Q: Why do I get different build results on my local machine versus CI/CD?
Usually it's different Node.js versions, missing environment variables, or platform-specific dependencies. Use an .nvmrc file to lock the Node.js version, and double-check that all required env vars are set in CI/CD.
Q: How do I debug a build error that only shows "Build failed" with no details?
Run with verbose output: next build --debug. You can also set NODE_OPTIONS="--stack-trace-limit=100" for more detailed stack traces. Check the .next directory for partial build output that might give you clues.
Q: What is the best way to handle third-party scripts that cause build errors?
Use the next/script component with an appropriate loading strategy (afterInteractive, lazyOnload) instead of jamming scripts directly into your HTML. For packages that aren't SSR-compatible, dynamic imports with ssr: false are your friend.
Wrapping Up
Next.js build errors are annoying, but they're almost always fixable once you know where to look. The framework's error messages keep getting better, and the community has documented solutions for pretty much everything at this point. The biggest thing that helped me was understanding the server/client boundary — once that clicks, about half of these errors just make intuitive sense.
When errors do hit, read the message carefully, isolate the cause, and clear your caches. You'll get through it.
Sources
- Next.js Documentation — Error Handling — Official guide for handling errors in Next.js applications
- Next.js Documentation — Environment Variables — How to configure and use environment variables in Next.js
- Next.js Documentation — next/image — API reference for the Image component and common configuration issues
- Next.js GitHub Discussions — Community-driven solutions for build errors and edge cases