Next.js Image Optimization Error: Invalid src or missing loader
ImageOptimizationError — next/image cannot optimize the source
Verified against Next.js 14.2 docs: next/image component API reference, Next.js 15.0 docs: Image Optimization — remotePatterns, Next.js source: packages/next/src/server/image-optimizer.ts · Updated June 2026
> quick_fix
The next/image component cannot optimize the image because the source URL hostname is not in remotePatterns, the width/height props are missing for external images, or you are deploying to a platform that does not support the default image optimizer. Add the hostname to next.config.js remotePatterns, provide explicit dimensions, or set a custom loader.
// next.config.ts
const nextConfig = {
images: {
remotePatterns: [
{
protocol: 'https',
hostname: 'images.unsplash.com',
},
{
protocol: 'https',
hostname: '*.amazonaws.com',
},
],
},
};
export default nextConfig;What causes this error
The next/image component routes all images through Next.js's built-in image optimization API (/_next/image). This API resizes, compresses, and converts images to modern formats (WebP/AVIF) on-demand. For security, Next.js blocks optimization of external images unless their hostnames are explicitly allowlisted in next.config.js remotePatterns. Without this allowlist, any website could abuse your server as a free image proxy. The error also fires when width and height are missing for external images (Next.js cannot infer dimensions from a URL), when the src is an empty string or malformed URL, or when the deployment platform does not support the image optimization API.
How to fix it
- 01
step 1
Add the image hostname to remotePatterns
If the error says 'hostname is not configured under images in your next.config', add the domain to the remotePatterns array. Use wildcard patterns for CDNs with subdomains. The domains config is deprecated in Next.js 14+ — use remotePatterns instead.
// next.config.ts const nextConfig = { images: { remotePatterns: [ { protocol: 'https', hostname: 'cdn.example.com', pathname: '/images/**', }, { protocol: 'https', hostname: '**.cloudinary.com', // wildcard subdomain }, { protocol: 'https', hostname: 'lh3.googleusercontent.com', }, ], }, }; export default nextConfig; - 02
step 2
Provide width and height for external images
External images (URLs) require explicit width and height props because Next.js cannot read dimensions from a remote URL at build time. If you want responsive sizing, use the fill prop instead and set the parent container's dimensions with CSS.
import Image from 'next/image'; // Option 1: Explicit dimensions <Image src="https://cdn.example.com/photo.jpg" alt="Description" width={800} height={600} /> // Option 2: Fill mode — image fills its positioned parent <div style={{ position: 'relative', width: '100%', height: '400px' }}> <Image src="https://cdn.example.com/photo.jpg" alt="Description" fill style={{ objectFit: 'cover' }} /> </div> - 03
step 3
Fix the 'Invalid src' error
This error fires when the src prop is an empty string, undefined, null, or a relative URL without a leading slash. Ensure src always has a valid value. For dynamic images that may be null, provide a fallback or conditionally render the component.
import Image from 'next/image'; // BAD: src might be undefined // <Image src={user.avatar} alt="" width={48} height={48} /> // GOOD: fallback for missing images <Image src={user.avatar || '/default-avatar.png'} alt={user.name} width={48} height={48} /> // GOOD: conditionally render {user.avatar && ( <Image src={user.avatar} alt={user.name} width={48} height={48} /> )} - 04
step 4
Configure a custom loader for static exports or external CDNs
If you deploy with output: 'export' (static HTML), the built-in image optimizer is unavailable because there is no server. Use a custom loader that points to your image CDN, or set unoptimized: true to serve images as-is.
// next.config.ts — for static exports const nextConfig = { output: 'export', images: { unoptimized: true, // serve images as-is, no optimization }, }; export default nextConfig; // OR use a custom loader for Cloudinary/Imgix: // next.config.ts const nextConfig = { images: { loader: 'custom', loaderFile: './lib/image-loader.ts', }, }; export default nextConfig; // lib/image-loader.ts export default function cloudinaryLoader({ src, width, quality, }: { src: string; width: number; quality?: number; }) { const params = ['f_auto', 'c_limit', `w_${width}`, `q_${quality || 'auto'}`]; return `https://res.cloudinary.com/demo/image/upload/${params.join(',')}${src}`; }
How to verify the fix
- npm run build completes without image optimization errors.
- Images load correctly in the browser — check the Network tab to confirm they route through /_next/image (or your custom loader).
- View page source: img tags have srcset attributes with multiple sizes for responsive loading.
- No broken image icons appear on any page.
Why ImageOptimizationError happens at the runtime level
The next/image component delegates optimization to an internal API route at /_next/image. This API validates the source URL against the remotePatterns allowlist and checks that the requested width is in the configured deviceSizes or imageSizes array before using sharp to resize and compress. The error fires at validation — before any processing. If the hostname is missing from remotePatterns, the API returns 400. If width/height props are absent, Next.js cannot generate the srcset attribute and throws at build time. For static exports, the /_next/image endpoint does not exist at all.
Common debug mistakes for ImageOptimizationError
- Using the deprecated domains config instead of remotePatterns — domains still works in Next.js 14 but is less flexible and will be removed; remotePatterns supports wildcards and pathname restrictions.
- Setting width and height to 0 or passing them as strings instead of numbers — the component requires positive integer values or it throws an invalid dimensions error.
- Forgetting to add a new CDN hostname after migrating image storage — the images worked locally (no optimization in dev by default) but fail in production because the hostname is not in remotePatterns.
- Using fill prop without setting position: relative on the parent container — the image renders but overflows its container and overlaps other elements, which looks like a rendering bug rather than a configuration issue.
- Deploying with output: 'export' without setting unoptimized: true — the build succeeds but images fail at runtime because no server exists to handle /_next/image requests.
When ImageOptimizationError signals a deeper problem
The image optimization error reveals a broader tension in Next.js: the framework provides powerful server-side features (image optimization, ISR, server actions) that silently assume a Node.js server is running in production. Developers who deploy to static hosting (GitHub Pages, S3, Netlify with static export) discover these features do not work only after deploying. The image optimizer is the most common casualty because it is easy to add an Image component in development, see it work perfectly (dev mode skips optimization), and then hit errors in production. The fix is always to match your next/image config to your deployment target — server-hosted apps get full optimization, static exports need unoptimized: true or a third-party image CDN.
Editor's take
The next/image component is one of the best performance features in Next.js — automatic WebP/AVIF conversion, responsive srcset generation, lazy loading by default, and blur-up placeholders. But it is also one of the most frequent sources of build errors, and almost every error traces back to the same root cause: the developer added an Image component without configuring the image source.
The remotePatterns config is the one you will hit most. Every external image hostname needs to be explicitly listed. This feels tedious when you are pulling user avatars from Google, product images from Shopify, and hero images from Unsplash — each requires a separate remotePatterns entry. But it is a security boundary, not bureaucracy. Without it, your /_next/image endpoint becomes an open proxy.
The second most common mistake is missing dimensions. Local images (imported from the filesystem) get their width and height detected automatically at build time. External images (URLs) cannot be measured at build time, so you must provide width and height explicitly or use the fill prop. The fill prop is usually the better choice for responsive layouts — it makes the image fill its parent container, and you control sizing through CSS on the parent.
The deployment mismatch is the sneakiest variant. Everything works in development because next dev serves images without optimization. You deploy to Vercel and it works because Vercel runs the optimizer. You move to a static export or a Docker container without sharp installed, and every image breaks. The fix depends on your platform: install sharp for self-hosted, use unoptimized for static exports, or configure a loader for Cloudinary or Imgix.
By Bikram Nath · Curator · Updated June 2026
Frequently asked questions
Why does Next.js require remotePatterns instead of allowing any URL?
Security. Without an allowlist, attackers could use your /_next/image endpoint as a free image proxy — passing any URL and consuming your server's CPU and bandwidth for image processing. The remotePatterns config ensures only trusted sources are optimized. This is especially important on Vercel where image optimization counts against your plan limits.
What is the difference between remotePatterns and the deprecated domains config?
The domains config only matched exact hostnames. remotePatterns supports protocol filtering, wildcard subdomains (using **), and pathname restrictions. For example, remotePatterns can limit optimization to https://cdn.example.com/uploads/** while domains would allow any path on that host. Always use remotePatterns — domains may be removed in a future version.
Can I use next/image with SVG files?
Yes, but Next.js does not optimize SVGs — they pass through as-is because SVGs are already vector graphics. If you see a 'dangerous SVG' warning, it is because SVGs can contain embedded scripts. For trusted SVGs, add dangerouslyAllowSVG: true and contentDispositionType: 'attachment' to your images config, or use an img tag directly for SVG files.