Issue with groq query not returning desired result on front end

10 replies
Last updated: Mar 22, 2024
Hi, seem to be having an issue with a groq query I'm using on my site.
I have an "seoImage" schema, which just contains an alt string field, and the built in image field.

Across my entire site, I use this groq query:


seoImage {
       ...,
       image {
         asset-> {
           ...,
           metadata
         }
       }
     }
However, for one image, I don't get anything back from the result. I just get the asset reference back with a _type and _ref properties.

I've tried doing the exact query I'm running in the vision plugin inside sanity, and I get the desired result (The image data, along with the metadata of the image).

Why isn't this working on my front end? but is working for all my other images?
AI Update

This is a classic issue related to perspectives and how the Sanity API returns draft vs. published documents. The behavior you're seeing - where Vision returns the full image data but your frontend only gets _type and _ref - almost certainly means you're querying different document states in each environment.

The Problem

When you query in Vision (inside Sanity Studio), it defaults to the raw perspective, which returns all documents including drafts. However, as of API version 2025-02-19, the default perspective for client queries changed from raw to published. This is a recent breaking change.

If that one problematic image exists only as a draft (perhaps it was just uploaded or the document containing it hasn't been published yet), your frontend query with the published perspective won't find the asset document. Instead of following the reference with asset->, it just returns the raw reference object with _type and _ref.

The Solution

You need to explicitly set the perspective in your Sanity client configuration. Here are your options:

Option 1: Use previewDrafts perspective (recommended for preview environments)

const client = createClient({
  projectId: 'your-project-id',
  dataset: 'your-dataset',
  apiVersion: '2025-02-19',
  useCdn: false,
  perspective: 'previewDrafts' // This prioritizes drafts over published
})

Option 2: Use raw perspective (returns everything)

const client = createClient({
  projectId: 'your-project-id',
  dataset: 'your-dataset',
  apiVersion: '2025-02-19',
  useCdn: false,
  perspective: 'raw' // Returns all documents including drafts
})

Quick Debugging Steps

  1. Check if it's a draft issue: Look at the document containing that image in Studio - does it have a "Publish" button visible? If so, the image asset itself might only exist in the draft state.

  2. Verify the asset is published: Go to the Vision plugin and run:

    *[_type == "sanity.imageAsset" && _id == "YOUR_IMAGE_ASSET_ID"]

    If you only see a result with an ID starting with drafts., that's your issue.

  3. Publish the document: The simplest fix might be to just publish the document containing that image, which should publish the image asset reference as well.

For production environments, you typically want the published perspective (the new default), but make sure all your content is actually published. For preview/development environments, use previewDrafts to see draft content.

The perspectives documentation has more details on how these different views work.

To be clear, your document has:
"seoImage": {
  "image": {
    "asset": {
      "_ref": "..."
      "_type": "reference"
    }
  }
}
and
seoImage.image.asset->
isn't working?
Hi User, yes that was my issue.
I did however figure out what was causing this to not follow the reference:

my entire query was the following:


*[_type == 'product' && _id == $_id][0] {
			intro {
				title,
				content,
				seoImage {
					...,
					image {
						...,
						asset-> {
							...,
							metadata
						}
					}
				},
				button {
					...,
					link {
						type,
						type == "internal" => internalLink {
							reference-> {
								_type,
								"slug": slug.current,
							}
						},
						type == "external" => externalLink {
							url,
						},
					}
				},
                ...,
			},
		}
It was the last spread operator inside the intro object that was causing the issue. Removing this allows the full image data to come through.

I noticed it wasn't an issue with the image when I added in my button, and also found it wasn't following the internalUrl references. Again, this was fixed upon removed the final spread operator.
Ah yeah, the spread operator will overwrite.
If I'd put that spread operator right at the beginning, am I correct in assuming that wouldn't have caused an issue?
Yes
Awesome, thanks for clearing that up for me, sorry for wasting your time!!
No worries!
I'd like to do this exactly, but dynamically. Hardcoding in every instance where an image reference "could" be seems silly to me. Is doing this dynamically possible?
Not at the moment, sorry.
This is something we know is a problem, and we are actively working on possible solutions.

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?