afterbuild/ops
ERR-932/Next.js · Images
ERR-932
Invalid src prop on `next/image`, hostname is not configured under images in your `next.config`

appears when:After deploying to Vercel when the same <Image /> component rendered fine on localhost

Next.js Image not loading in production

The Vercel image optimizer only fetches hosts you explicitly allow. Every other remote src falls back to a blank box or a 400 from /_next/image.

Last updated 17 April 2026 · 5 min read · By Hyder Shah
Direct answer
next/image not loading in production is almost always an allow-list problem. Add the image hostname to images.remotePatterns (preferred) or images.domains (legacy) in next.config.ts. If the optimizer still 400s, check your Vercel transform quota and the CORS headers on the source bucket.

Quick fix for next/image not loading

next.config.ts
typescript
01// next.config.ts — allow Supabase Storage and Cloudinary in next/image02import type { NextConfig } from "next";03 04const config: NextConfig = {05  images: {06    remotePatterns: [07      {08        protocol: "https",09        hostname: "*.supabase.co",10        pathname: "/storage/v1/object/public/**",11      },12      {13        protocol: "https",14        hostname: "res.cloudinary.com",15      },16      {17        protocol: "https",18        hostname: "images.unsplash.com",19      },20    ],21    // opt-out per image: <Image unoptimized src={...} />22    // or globally if you are over the Vercel transform quota23    // unoptimized: true,24  },25};26 27export default config;
Paste into next.config.ts — covers Supabase Storage, Cloudinary, and Unsplash with a single redeploy

Deeper fixes when the quick fix fails

01 · Over the Vercel transform quota — disable optimization for the page

app/gallery/page.tsx
typescript
01// app/gallery/page.tsx — unoptimized on a page that drives quota usage02import Image from "next/image";03 04export default function Gallery({ photos }: { photos: { url: string; alt: string }[] }) {05  return (06    <div className="grid grid-cols-3 gap-4">07      {photos.map((p) => (08        <Image09          key={p.url}10          src={p.url}11          alt={p.alt}12          width={600}13          height={400}14          unoptimized // skip /_next/image — serve the origin URL directly15        />16      ))}17    </div>18  );19}
Use unoptimized per image when Cloudinary or Bunny is already transforming for you

02 · Fix CORS on the source bucket (Supabase Storage)

The Vercel optimizer fetches with an Origin header. If your bucket returns Access-Control-Allow-Origin for a different origin, the fetch fails. Supabase Storage public buckets default to permissive CORS, but a custom policy can block the optimizer. Confirm the bucket is public and the policy allows GET from *.

supabase/migrations/bucket_public_read.sql
sql
01-- Supabase SQL editor — make bucket public and allow anonymous reads02update storage.buckets set public = true where id = 'avatars';03 04create policy "public read"05on storage.objects06for select07to public08using (bucket_id = 'avatars');

03 · Self-hosted — configure a loader

next.config.ts
typescript
01// next.config.ts — custom loader for self-hosted without Vercel optimizer02import type { NextConfig } from "next";03 04const config: NextConfig = {05  images: {06    loader: "custom",07    loaderFile: "./image-loader.ts",08  },09};10 11export default config;
Custom loaders let you route through Cloudinary, Imgix, or your own CDN instead of Vercel

Why AI-built apps hit next/image not loading

next/image is not a drop-in replacement for a plain <img> tag. Every render routes through /_next/image, a serverless function that fetches the origin, resizes, converts to AVIF or WebP, and caches. To prevent abuse, the optimizer refuses to fetch any host not listed in images.remotePatterns. The first time your app hits an unlisted hostname the optimizer returns a 400, the component renders an empty box, and the layout shifts or collapses.

AI builders generate <Image src={supabaseUrl} /> or embed Unsplash hotlinks without touching next.config.ts. In the preview the dev server is lenient and fetches anyway. In production on Vercel the optimizer enforces the list. Same source code, different behavior. The fix is additive: list the hostname with a glob for the bucket path and redeploy.

The second common cause is Vercel's monthly image transform quota. On the Hobby plan the cap is a thousand transforms per project. Once exceeded the optimizer returns 402 Payment Required. The image simply stops loading for the rest of the billing cycle. Either upgrade the plan, move to unoptimized mode, or serve images through a CDN like Cloudinary or Bunny that does its own transformation outside the Vercel quota.

The third source is bad prop combinations. fill requires a positioned parent with explicit dimensions. width and height must be numbers not strings. Mixing fill with width throws at render time. AI generators emit half the props, so the component either errors in strict mode or silently renders a zero-height element.

next/image not loading by AI builder

How often each AI builder ships this error and the pattern that produces it.

AI builder × next/image not loading
BuilderFrequencyPattern
LovableEvery Supabase Storage scaffoldUses <Image> with Supabase URL; never edits next.config
Bolt.newCommonHotlinks Unsplash without remotePatterns entry
v0CommonEmits <Image fill /> without a positioned parent
CursorSometimesUses string width='300' instead of number width={300}
Replit AgentRareDisables optimization globally hiding real errors

Related errors we fix

Stop next/image not loading recurring in AI-built apps

Still stuck with next/image not loading?

Emergency triage · $299 · 48h turnaround
We restore service and write the root-cause report.
start the triage →

next/image not loading questions

Why does next/image render blank in production but fine locally?+
next/image runs every remote src through the Vercel image optimizer, which refuses to fetch hosts not listed in images.remotePatterns (Next 13+) or images.domains (legacy). Locally the dev server is more permissive and often proxies anyway, hiding the config gap. In production the optimizer returns a 400 or 402 and the component renders an empty box. Add the hostname to remotePatterns and redeploy.
What is the difference between images.domains and images.remotePatterns?+
images.domains is the legacy string allow-list and still works but is frozen. images.remotePatterns replaces it with richer matching: protocol, hostname, port, and pathname globs. remotePatterns is required if you need to restrict by path prefix or match https only. New Next.js projects should use remotePatterns exclusively. Mixing both is legal but confusing — pick one and delete the other.
Why does Vercel return a 402 Payment Required for my image?+
Vercel's image optimization has a per-project monthly transform quota. On the Hobby plan that cap is 1,000 transforms. Once exceeded, the optimizer returns 402 and the image fails. Confirm in the Vercel dashboard under Usage. Fix by upgrading the plan, setting images.unoptimized=true for static pages, or serving the image from a CDN like Cloudinary that optimizes on its own.
Do I need both width and height on next/image?+
Yes for static imports or remote images without a layout wrapper. The component needs to reserve space to prevent layout shift. If you do not know the dimensions up front, use the fill prop with a relatively positioned parent that has an explicit size via CSS. Mixing fill with width and height is a runtime error. AI generators often emit <Image src={url} /> with no sizing at all, which throws in strict mode.
How long does a Next.js image fix take?+
Ten minutes for a single missing hostname in remotePatterns. Twenty minutes if CORS on the source also needs work. Longer if you hit the Vercel transform quota and need to migrate off image optimization. Emergency Triage at $299 covers the typical fix plus a Playwright test that screenshots each image to catch regressions before the next deploy.
Next step

Ship the fix. Keep the fix.

Emergency Triage restores service in 48 hours. Break the Fix Loop rebuilds CI so this error cannot ship again.

About the author

Hyder Shah leads Afterbuild Labs, shipping production rescues for apps built in Lovable, Bolt.new, Cursor, Replit, v0, and Base44. our rescue methodology.

Sources