How to query images from portable text body in Sanity slug page?

25 replies
Last updated: Jan 28, 2026
I'm trying to get the images from inside the body of a [slug].js in my blog, but I'm not getting it right ... can someone help me with some clues?So far I'm stuck at
ReferenceError: image is not defined. I've tried a few different ways to get a hold of the image.. but no luck so far..
AI Update

It sounds like you're trying to query images that are embedded inside a Portable Text body field in your blog posts, but you're getting a "ReferenceError: image is not defined" error. This is a common issue when working with Sanity's Portable Text and images.

The problem is likely that when you query your blog post, the image references inside the body field aren't being "expanded" - they're just coming back as reference objects rather than the full image data you need.

The Solution: Use GROQ to Expand Image References

When you have images inside a Portable Text field (like your body array), you need to use GROQ's projection syntax to "materialize" or expand those image asset references. Here's how to do it:

*[_type == "post" && slug.current == $slug][0] {
  title,
  body[]{
    ...,
    _type == "image" => {
      ...,
      asset->
    }
  }
}

Let me break this down:

  • body[] - iterate through each block in the body array
  • ... - spread all the existing properties
  • _type == "image" => { ... } - when you encounter an image block, expand it
  • asset-> - follow the asset reference to get the full image document

For Next.js (getStaticProps or similar)

If you're using this in a [slug].js file with Next.js, your query might look like:

export async function getStaticProps({ params }) {
  const query = `*[_type == "post" && slug.current == $slug][0] {
    title,
    body[]{
      ...,
      _type == "image" => {
        ...,
        asset->{
          _id,
          url,
          metadata {
            dimensions
          }
        }
      }
    }
  }`
  
  const post = await client.fetch(query, { slug: params.slug })
  
  return {
    props: { post }
  }
}

Rendering the Images

Once you have the expanded data, you can render images in your Portable Text using @portabletext/react:

import { PortableText } from '@portabletext/react'
import imageUrlBuilder from '@sanity/image-url'

const builder = imageUrlBuilder(client)

const components = {
  types: {
    image: ({ value }) => (
      <img 
        src={builder.image(value).width(800).url()} 
        alt={value.alt || ' '} 
      />
    )
  }
}

export default function Post({ post }) {
  return (
    <PortableText 
      value={post.body} 
      components={components} 
    />
  )
}

The key issue was that you need to explicitly expand the image asset references in your GROQ query using the asset-> syntax. Without this, you just get a reference object, not the actual image data you need to display the image.

Check out the official Sanity docs on Block Content image materializing for more details on this pattern!

Show original thread
25 replies

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?