Next.js App Router Images Not Loading in Production
After migrating to Next.js 13+ with the new App Router, images that load correctly in development are not loading in the production build. The <Image />
component shows broken images or 404 errors, even though the paths are correct. What could be causing this issue and how can it be resolved?
Solution
This issue is often caused by misconfiguration of the next.config.js
file or incorrect usage of the Image
component with the new App Router. Here are some steps to resolve it:
- Check the
images.domains
configuration: Ensure that all external domains used for images are listed in theimages.domains
array in yournext.config.js
. - Use the correct
src
path: For static images in thepublic
folder, use/image.png
(with a leading slash). For images from remote sources, ensure the domain is allowed. - Deployment platform settings: If deploying to Vercel or another platform, make sure the
public
directory is included and not ignored by.gitignore
or build settings. - Clear build cache: Sometimes, a stale build cache can cause image issues. Run
next build && next start
after clearing.next
.
Example next.config.js
:
module.exports = {
images: {
domains: ['yourdomain.com', 'anotherdomain.com'],
},
};
By following these steps, you should be able to resolve most image loading issues in production with Next.js App Router.
Alternative #1
I've encountered this issue in production deployments, and sometimes the problem is with image optimization settings or CDN configuration. If you're using a custom CDN or have specific image optimization requirements, you might need to configure the images.loader
:
// next.config.js
module.exports = {
images: {
loader: 'custom',
loaderFile: './image-loader.js',
},
};
Then create a custom loader:
// image-loader.js
export default function imageLoader({ src, width, quality }) {
return `https://your-cdn.com/${src}?w=${width}&q=${quality || 75}`;
}
This gives you full control over how images are served and optimized.
Alternative #2
Another common cause I've seen is environment-specific image paths. If your images are stored differently in development vs production, you can use environment variables:
// In your component
import Image from 'next/image';
const imagePath = process.env.NODE_ENV === 'production'
? 'https://your-production-domain.com/images/logo.png'
: '/images/logo.png';
<Image src={imagePath} alt="Logo" width={200} height={100} />
Or use a helper function:
// utils/images.js
export function getImagePath(path) {
if (process.env.NODE_ENV === 'production') {
return `https://your-cdn.com${path}`;
}
return path;
}
This ensures images load correctly in both environments.
Alternative #3
If you're using static exports or deploying to platforms that don't support Next.js image optimization, you might need to disable the Image component optimization:
// next.config.js
module.exports = {
output: 'export',
images: {
unoptimized: true,
},
};
Or use regular <img>
tags for static deployments:
// For static exports
<img src="/images/logo.png" alt="Logo" width={200} height={100} />
// For dynamic deployments
<Image src="/images/logo.png" alt="Logo" width={200} height={100} />
This is especially important when deploying to platforms like Netlify or GitHub Pages that don't support Next.js server-side features.