Issue with displaying images in a website using Sanity.io schema and backend response.

2 replies
Last updated: Sep 28, 2023
Hi Team,
I'm having some errors with displaying images on my website and I'll try to be as specific as possible.

In my schema, I have this:
{
name: "content",
title: "Content",
type: "array",
of: [{ type: "block" }, { type: "image" }],
},

Within sanity studio, I can see the image and when checking with the inspector, I can also see that it has a full cdn url. That's perfect.

The image property on the backend response however is showing up like this (i am using postman):

{
"_type": "image",
"_key": "00f00d67d82b",
"asset": {
"_ref": "image-386f55d56fc3147ea64116cd7365408f557a36a1-1024x1024-png",
"_type": "reference"
}
},

i need this string to be the full cdn url. Otherwise I get an error:

Error: Failed to parse src "image-386f55d56fc3147ea64116cd7365408f557a36a1-1024x1024-png" on
next/image
, if using relative image it must start with a leading slash "/" or be an absolute URL (http:// or https://)
This issue is only happening within the content block. i have a separate image field that works fine and brings in the full cdn url, so it displays as it should on the front end.

Looking forward to hearing any of your thoughts!
AI Update

This is a common issue when working with Portable Text (block content) that contains images! The problem is that Sanity returns image references (just the _ref and _type) by default, not the full CDN URLs. You need to expand these references in your GROQ query.

Update Your GROQ Query

Instead of just querying the content field directly, you need to explicitly expand the image assets within the array. Here's an example query:

*[_type == "yourDocumentType"] {
  ...,
  content[] {
    ...,
    _type == "image" => {
      ...,
      asset->
    }
  }
}

The key part is asset-> which tells GROQ to follow the reference and fetch the actual asset document, which includes the url field.

For Next.js Image Component

If you're using Next.js, you'll still want to use @sanity/image-url to build optimized URLs with transformations. When rendering your Portable Text, you'll need a custom serializer for image blocks:

import imageUrlBuilder from '@sanity/image-url'
import Image from 'next/image'

const builder = imageUrlBuilder(client)

const PortableTextComponents = {
  types: {
    image: ({value}) => {
      const imageUrl = builder.image(value).width(800).url()
      return (
        <Image 
          src={imageUrl}
          alt={value.alt || ''}
          width={800}
          height={600}
        />
      )
    }
  }
}

Why Your Separate Image Field Works

Your standalone image field likely works because you're either:

  1. Using a different GROQ query that expands the reference with asset->
  2. Using @sanity/image-url to build the URL from the reference

The same principles apply to images within Portable Text arrays - you just need to handle them in your query and renderer.

Make sure you've also configured your next.config.js to allow images from Sanity's CDN:

images: {
  remotePatterns: [
    { protocol: 'https', hostname: 'cdn.sanity.io' }
  ]
}

This should resolve your issue! The key takeaway is that images in Portable Text need special handling both in your query (expanding references with asset->) and in your rendering logic (using a custom serializer with @sanity/image-url).

You can pass the full asset to the image url builder to get the url.
thank you, rd! that did the trick

Sanity – Build the way you think, not the way your CMS thinks

Sanity is the developer-first content operating system that gives you complete control. Schema-as-code, GROQ queries, and real-time APIs mean no more workarounds or waiting for deployments. Free to start, scale as you grow.

Was this answer helpful?