Next.js error
Failed to Parse src on next/image
The error
Error: Failed to parse src "products/photo.jpg" on `next/image`, if using relative image it must start with a leading slash "/" or be an absolute URL (http:// or https://)A path that works in an ordinary img tag fails here, because the optimiser needs to resolve it without a document to resolve it against.
next/image does not hand the path to the browser. It resolves it, fetches
the file, resizes it and caches the result - and it does that without a
document to resolve a relative path against. So products/photo.jpg is
ambiguous in a way that /products/photo.jpg is not.
The fix
For a file in public/, use the path from the public root, with the leading
slash:
// public/products/photo.jpg
<Image src="/products/photo.jpg" alt="" width={800} height={600} />For a remote file, use the full URL - and remember the host has to be allowed, which is a different error with its own reason.
For an imported image, import it and pass the object. This is the best option where the file is part of the repository, because the dimensions come with it:
import photo from './photo.jpg';
<Image src={photo} alt="" placeholder="blur" />;No width and height needed, no chance of them being wrong, and a blur
placeholder for free.
The version that reaches production
The literal-string case is caught the first time anyone loads the page. The
one that survives is a dynamic src:
<Image src={product.imageUrl} alt={product.name} width={400} height={400} />This works for every record that has an image. It throws for the ones that do not, or that store a path without the leading slash, or that store a full URL for some rows and a relative path for others - which is the normal state of a CMS field that has been edited by more than one person over more than one year.
Normalise at the boundary, where the data enters the component:
function imageSrc(value: string | null | undefined): string | null {
if (!value) return null;
if (value.startsWith('http://') || value.startsWith('https://')) return value;
return value.startsWith('/') ? value : `/${value}`;
}Then render nothing, or a placeholder, when it returns null. An image
component that throws on missing data takes the whole route down with it;
one that renders a placeholder does not.
The dimension trap next to it
While you are here: a fixed width and height that do not match the file's
aspect ratio will not error - it will distort the image, or reserve the wrong
space and produce a layout shift when it loads. Imported images avoid this
entirely. Remote ones need the real numbers, or fill with a sized parent.
Reserved space is most of what keeps Cumulative Layout Shift under control, and next/image will not fix your LCP on its own - but getting these two attributes wrong reliably makes it worse.
