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:

  1. Check the images.domains configuration: Ensure that all external domains used for images are listed in the images.domains array in your next.config.js.
  2. Use the correct src path: For static images in the public folder, use /image.png (with a leading slash). For images from remote sources, ensure the domain is allowed.
  3. 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.
  4. 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.

Last modified: July 3, 2025
Stay in the loop
Subscribe to our newsletter to get the latest articles delivered to your inbox