How to filter out null responses in GROQ query when collecting tags?

24 replies
Last updated: Jan 14, 2026
Hello! I am wondering how I can filter out all
null
responses in this GROQ query…My goal is to basically sift thru my entire response and get all the “tags” from blog, image, and imageCollections.

*[_type == "blogPost" && _id =="e79ed544-8747-4114-9461-3935791eb7bc"]{
  "blogTags": tags[]->{"slug": slug.current, tagName},
    
  "mainImageTags": mainImage->.tags[]->{"slug": slug.current, tagName},
    
  "imageCollectionTags": body[]{
        _type == 'imageCollectionRef' => @->{
          collectionImages[]->{tags[]}
        }
  }.collectionImages[].tags[]->{"slug": slug.current, tagName},
}
AI Update

Great question! Filtering out null values in GROQ can be done using the defined() function. Looking at your query, you're getting nulls because some of those reference chains or array traversals might not exist on every document.

Here are a few approaches to filter out nulls:

Option 1: Use defined() in array filters

You can filter arrays as you traverse them by adding defined() checks:

*[_type == "blogPost" && _id =="e79ed544-8747-4114-9461-3935791eb7bc"]{
  "blogTags": tags[defined(@)]->{"slug": slug.current, tagName},
    
  "mainImageTags": mainImage.tags[defined(@)]->{"slug": slug.current, tagName},
    
  "imageCollectionTags": body[_type == 'imageCollectionRef']@->{
    collectionImages[]->{tags[defined(@)]}
  }.collectionImages[].tags[defined(@)]->{"slug": slug.current, tagName},
}

The @ symbol refers to the current element being evaluated in the array.

Option 2: Flatten and filter at the end

If you want to collect all tags into a single array and filter nulls, you can use array flattening:

*[_type == "blogPost" && _id =="e79ed544-8747-4114-9461-3935791eb7bc"]{
  "allTags": [
    ...tags[]->{"slug": slug.current, tagName},
    ...mainImage.tags[]->{"slug": slug.current, tagName},
    ...body[_type == 'imageCollectionRef']@->.collectionImages[].tags[]->{"slug": slug.current, tagName}
  ][defined(slug)]
}

The ... spread operator flattens arrays, and [defined(slug)] filters out any null entries (since tags without a slug are likely null).

Option 3: Use select() with defined() for conditional inclusion

For more complex conditional logic, you can use the select() function:

*[_type == "blogPost" && _id =="e79ed544-8747-4114-9461-3935791eb7bc"]{
  "blogTags": select(
    defined(tags) => tags[]->{"slug": slug.current, tagName},
    []
  ),
  // ... rest of query
}

The key insight is that defined() returns true if a property exists and is not null, making it perfect for filtering out those unwanted null values. You can apply it at different stages of your query depending on where the nulls are appearing!

Show original thread
24 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?