Next.js Image Component Not Rendering: Common Causes and Fixes
The Next.js Image component fails silently when required dimensions are missing, remote patterns are unconfigured, or the image is rendered in static export mode without disabling optimization.
The <Image /> component in the vercel/next.js repository is designed to optimize performance by building responsive srcSet attributes and handling lazy loading automatically. However, its architecture deliberately suppresses errors in production to prevent page crashes, which often results in blank or zero-size images when configuration is incorrect. Understanding the validation logic inside getImgProps and the rendering pipeline in image-component.tsx is essential for debugging these silent failures.
Why the Next.js Image Component Fails Silently
The component acts as a thin wrapper that constructs <img> attributes on both server and client. When validation fails—such as missing dimensions or disallowed remote hosts—the code in packages/next/src/shared/lib/get-img-props.ts forces the image into unoptimized mode or strips the src attribute entirely to avoid breaking the layout. In development, this triggers clear error messages, but in production builds, the component renders a hidden or empty image element instead of crashing the page.
Common Reasons Why Images Are Not Rendering
Missing Width and Height Properties
For non-fill images, getImgProps validates widthInt and heightInt. If these integers are missing, the component throws an error in development. In production, it falls back to unoptimized mode, which removes the srcSet and can result in a blank image if the src is also malformed.
Always supply explicit numeric width and height props, or use the fill property for responsive layouts.
Using Fill Mode Without Relative Positioning
When fill is used, the component forces position: absolute; width: 100%; height: 100% and checks the parent element's position property. If the parent is not relative, absolute, or fixed, the image may be positioned incorrectly or hidden from view.
Wrap <Image fill ... /> in a container with position: relative and explicit dimensions.
Unconfigured Remote Patterns
The server-side validator in the image optimization API rejects URLs that do not match a configured remotePatterns entry. When this happens, getImgProps forces the image to unoptimized, which can leave the src empty if the optimizer endpoint was expected to provide the URL.
Add the remote host to remotePatterns in next.config.js:
module.exports = {
images: {
remotePatterns: [
{
protocol: 'https',
hostname: 'images.unsplash.com',
},
],
},
}
Static Export Without Unoptimized Flag
When running next export, the built-in optimizer cannot function because it requires a server. getImgProps detects config.output === 'export' and throws an error unless images.unoptimized is explicitly set to true.
Disable optimization for static exports:
module.exports = {
images: {
unoptimized: true,
},
}
Missing Blur Data URL for Placeholder
Using placeholder="blur" requires a blurDataURL. In development, getImgProps throws a clear error if this is missing. In production, the placeholder remains empty, leaving the image invisible until the real source loads—which may never happen if the source is invalid.
Import the image statically to automatically receive blurDataURL, or provide a manually generated data URL.
Custom Loader Not Implementing Width
When a custom loader is supplied, getImgProps checks that the returned URL contains the width query parameter. If not, a warning is emitted and the image is treated as unoptimized, resulting in a missing srcSet and potential blank render.
Ensure the custom loader returns a URL containing ?w=<width>:
export default function myLoader({ src, width, quality }) {
return `https://img.example.com/${src}?w=${width}&q=${quality || 75}`
}
SVG Handling Without Dangerously Allow SVG
The default loader treats SVGs as unoptimized. If dangerouslyAllowSVG is false (the default), the component may skip the optimization pipeline. When the SVG is served from the internal image-optimizer endpoint without proper configuration, the result can be a blank image.
Enable SVG support in next.config.js or serve SVGs as static files:
module.exports = {
images: {
dangerouslyAllowSVG: true,
contentSecurityPolicy: "default-src 'self'; script-src 'none'; sandbox;",
},
}
Invalid Source Strings
getImgProps validates the src string and throws an error in development if it contains leading/trailing whitespace or control characters. In production, the URL is silently dropped, resulting in an image with no src.
Sanitize the source by trimming whitespace or using encodeURIComponent for dynamic paths.
How the Image Component Processes Props
Understanding the internal pipeline helps diagnose why images disappear. The architecture spans several key files in the vercel/next.js repository:
-
Entry Point – The client-side component lives in
packages/next/src/client/image-component.tsx. It receives props and delegates processing togetImgProps. -
Prop Processing –
packages/next/src/shared/lib/get-img-props.tscontains the core validation logic. It merges global configuration frompackages/next/src/shared/lib/image-config.ts, validates required fields likewidthIntandheightInt, and determines whether the image should be treated as unoptimized. -
Attribute Generation – Based on the loader choice (default or custom), the code generates
src,srcSet, andsizesattributes. If the loader returns a URL without width parameters, the component falls back to unoptimized mode. -
Placeholder Handling – When
placeholder="blur"is used,packages/next/src/shared/lib/image-blur-svg.tsgenerates a tiny SVG for the blur effect. IfblurDataURLis missing, the placeholder remains empty. -
Server-Side Validation – The image optimization API in
packages/next/src/server/og/image-response.ts(and related server files) validates remote URLs againstremotePatterns. Rejected URLs force the client to treat the image as unoptimized.
If any step in this pipeline fails—whether through missing dimensions, misconfigured loaders, or disallowed remote hosts—the component either throws an error in development or silently renders an empty image element in production.
Code Examples and Solutions
Correct Static Import Usage
Static imports automatically provide width, height, and blurDataURL.
import Image from 'next/image'
import photo from '@/public/me.jpg'
export default function Avatar() {
return (
<Image
src={photo}
alt="Me"
placeholder="blur"
priority
/>
)
}
Proper Fill Implementation
Always use a relative positioned parent with explicit dimensions.
<div style={{ position: 'relative', width: '100%', height: '300px' }}>
<Image
src="/landscape.jpg"
alt="Landscape"
fill
style={{ objectFit: 'cover' }}
/>
</div>
Configuring Remote Patterns
Allow external domains explicitly in your configuration.
// next.config.js
module.exports = {
images: {
remotePatterns: [
{
protocol: 'https',
hostname: 'images.unsplash.com',
},
],
},
}
Custom Loader Setup
Ensure width parameters are included in the returned URL.
// my-image-loader.js
export default function myLoader({ src, width, quality }) {
return `https://img.example.com/${src}?w=${width}&q=${quality || 75}`
}
Disabling Optimization for Static Export
Required when using next export without a server.
// next.config.js
module.exports = {
images: {
unoptimized: true,
},
}
Summary
- The Next.js Image component validates props in
getImgPropsand fails silently in production when configuration is invalid, resulting in blank images. - Always provide
widthandheight(or usefillwith a relative parent) to prevent the component from falling back to unoptimized mode without a validsrcSet. - Configure
remotePatternsinnext.config.jsfor external images; otherwise the optimizer rejects the URL and the image may not render. - Set
unoptimized: truewhen usingnext exportbecause the built-in optimizer requires a server environment. - Include width parameters in custom loaders and provide
blurDataURLwhen usingplaceholder="blur"to avoid empty placeholders.
Frequently Asked Questions
Why is my Next.js image blank even with width and height?
If you are using a remote image, the blank render usually indicates that the domain is not listed in remotePatterns in next.config.js. According to the source code in get-img-props.ts, when a remote URL fails validation, the component forces unoptimized mode, which can strip the src if the loader expects an optimized endpoint. Verify your remotePatterns configuration and ensure the URL matches the protocol and hostname exactly.
Does Next.js Image work with static export?
The Image component works with next export, but the built-in optimizer does not. As implemented in get-img-props.ts, the code detects config.output === 'export' and throws an error unless you set images.unoptimized: true in next.config.js. With unoptimized: true, the component renders a standard <img> tag with the original src URL, bypassing the optimization API entirely.
Why does fill mode make my image disappear?
The fill property applies position: absolute to the image and requires the parent container to establish a positioning context. If the parent lacks position: relative, absolute, or fixed, the image may collapse to zero size or position itself incorrectly in the DOM. The component checks for this in development and emits a warning, but in production the image simply appears blank. Always wrap fill images in a container with explicit dimensions and position: relative.
How do I fix remote images not loading in Next.js?
Remote images require explicit permission via the remotePatterns configuration in next.config.js. The validation logic in the image optimization API rejects any URL that does not match a configured pattern, causing the client-side component to receive an empty or unoptimized response. To fix this, add an entry specifying the protocol and hostname for your image CDN. For example, add { protocol: 'https', hostname: 'cdn.example.com' } to allow images from that domain to be optimized and rendered.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →