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.
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
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;Deeper fixes when the quick fix fails
01 · Over the Vercel transform quota — disable optimization for the page
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}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 *.
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
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;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.
| Builder | Frequency | Pattern |
|---|---|---|
| Lovable | Every Supabase Storage scaffold | Uses <Image> with Supabase URL; never edits next.config |
| Bolt.new | Common | Hotlinks Unsplash without remotePatterns entry |
| v0 | Common | Emits <Image fill /> without a positioned parent |
| Cursor | Sometimes | Uses string width='300' instead of number width={300} |
| Replit Agent | Rare | Disables optimization globally hiding real errors |
Related errors we fix
Stop next/image not loading recurring in AI-built apps
- →Commit a typed hostname allow-list in next.config.ts with a comment next to each entry.
- →Write a Playwright screenshot test for every hero image so purge or quota regressions fail CI.
- →Prefer a dedicated image CDN (Cloudinary, Bunny) when you are near the Vercel transform quota.
- →Treat /_next/image 400s as build failures — add a lint rule that fails on missing remotePatterns.
- →Document the bucket CORS policy alongside the storage schema in your migration folder.
Still stuck with next/image not loading?
next/image not loading questions
Why does next/image render blank in production but fine locally?+
What is the difference between images.domains and images.remotePatterns?+
Why does Vercel return a 402 Payment Required for my image?+
Do I need both width and height on next/image?+
How long does a Next.js image fix take?+
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.
Hyder Shah leads Afterbuild Labs, shipping production rescues for apps built in Lovable, Bolt.new, Cursor, Replit, v0, and Base44. our rescue methodology.