Skip to content

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.

Related questions

Why does the same path work in a plain img tag?
A browser resolves a relative path against the current document URL. next/image runs before there is a document - the optimiser needs an absolute reference to fetch and cache the file.
The src comes from a CMS and is sometimes empty. What then?
Guard it. An empty string produces this error at runtime on exactly the records that lack an image, which is usually discovered in production.