Querying images used in a document in a schema-agnostic way in Slack thread.

4 replies
Last updated: Oct 8, 2023
Hey folks - I'm building a custom color input with recommended swatches based on the images used in a document.
It's working great - except I haven't been successful in querying images used in a document. It'd be easy to make it schema specific, but I would like this to operate agnostic of the document schema, especially since my project uses portable text with image fields extensively.

I've tried
*[_type == 'sanity.imageAsset' && references($docId)]
but it runs an empty array despite at least one image being used on my document.
Seems like
sanity.imageAsset
isn't returned in this way whatsoever.
Is there any method for doing this sort of thing or am I right out of luck? Thanks!!
AI Update

I understand your challenge! You're trying to query all images used in a document in a schema-agnostic way, but *[_type == 'sanity.imageAsset' && references($docId)] isn't working as expected.

The issue is that image assets don't directly reference your documents - it's the other way around. Your documents contain references to image assets (through the asset._ref field in image objects). So the references() function won't work in this direction.

Why your query didn't work

The references() function checks if a document contains a reference TO another document. According to the GROQ documentation, when you use references(), you're asking "which documents reference this ID?"

Image assets (sanity.imageAsset) are stored separately and don't contain references back to documents that use them - they're just storage objects. Your documents reference the assets, not vice versa. That's why your query returns an empty array.

Solutions

Option 1: Query the document and extract image references

You can fetch the document and traverse it to find all image asset references:

*[_id == $docId][0]{
  "imageRefs": [
    // Direct image fields
    mainImage.asset._ref,
    // Images in arrays
    ...images[].asset._ref,
    // Images in portable text
    ...content[_type == "image"].asset._ref,
    // Add other known patterns
  ]
}

Then fetch the actual assets:

*[_type == "sanity.imageAsset" && _id in $imageRefs]

The challenge is you need to know where images might appear in your schema.

For a truly schema-agnostic solution, fetch the entire document and parse it client-side:

function findAllImageRefs(obj, refs = new Set()) {
  if (!obj || typeof obj !== 'object') return refs;
  
  // Check if this is an image object with an asset reference
  if (obj._type === 'image' && obj.asset?._ref) {
    refs.add(obj.asset._ref);
  }
  
  // Recursively traverse all properties
  Object.values(obj).forEach(value => {
    if (Array.isArray(value)) {
      value.forEach(item => findAllImageRefs(item, refs));
    } else if (typeof value === 'object') {
      findAllImageRefs(value, refs);
    }
  });
  
  return refs;
}

// Usage in your custom input component
const doc = await client.fetch('*[_id == $docId][0]', { docId });
const imageRefs = Array.from(findAllImageRefs(doc));
const assets = await client.fetch(
  '*[_type == "sanity.imageAsset" && _id in $refs]',
  { refs: imageRefs }
);

This approach:

  • Works with any schema structure
  • Handles portable text with embedded images
  • Finds images in nested arrays and objects
  • Requires no schema knowledge

Option 3: Hybrid approach

If you want some GROQ optimization but still need flexibility, you could use GROQ's array flattening with the ellipsis operator:

*[_id == $docId][0]{
  ...,
  "allImageRefs": [
    ...(*[@ references(^._id)][]...[_type == "image"].asset._ref)
  ]
}

Though this still has limitations with deeply nested structures.

Best recommendation for your use case

Since you're building a custom color input that needs to work across different schemas and handle portable text, Option 2 (client-side parsing) is your best bet. It's truly schema-agnostic, handles all edge cases, and gives you complete control. You can then extract colors from those images to populate your color picker swatches.

Hey
user Q
. The query you wrote means "if type is
sanity.imageAsset
and it references the document ID", which is the opposite order of what you want, I believe.I'm going out on a limb here, but maybe
*[_type == "sanity.imageAsset" && count(*[_id == $docId && references(^._id)]) > 0]
might work
You are amazing. Thank you!!
I had some variants of that but didn't attempt the greaterThan properly
Haha I'm glad it worked

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?