Issue with reference fields not returning expected data in GROQ query.

13 replies
Last updated: Oct 26, 2022
Hey! I am struggling with following reference fields when they are two arrays deep. I have this query:
*[_type == "landingPage" && slug.current == "get-started"][0]{

    landingPageBuilder[] {
      _type == 'industries' => {
          industries[]->{title}
      },
    ...
    }
  }
I would expect that each of the industries (which are an array of references) would be returned with just the
title
. But I only get back the
_ref
,
_key
, and
_type
without the title. Can anyone spot what I am doing wrong?
AI Update

I can see your query syntax industries[]->{title} is actually correct GROQ! The issue is with how conditional projections work within nested array projections.

The problem is that when you use _type == 'industries' => {...} inside an array projection, the conditional completely replaces the object when true. Without the spread operator (...), you only get back what you explicitly define in that conditional block, which is why you're seeing just _ref, _key, and _type without the dereferenced titles.

Here's the corrected query:

*[_type == "landingPage" && slug.current == "get-started"][0]{
  landingPageBuilder[] {
    _type == 'industries' => {
      ...,
      industries[]->{title}
    }
  }
}

Why this works:

  1. The spread operator (...) preserves all the original fields from the landingPageBuilder item
  2. Your syntax industries[]->{title} is valid GROQ - it dereferences each reference in the array and projects only the title field
  3. According to the GROQ joins documentation, the pattern array[]->{fields} correctly dereferences arrays of references with projection

Alternative if you only need title strings:

If you want a flat array of title strings instead of objects with title properties:

*[_type == "landingPage" && slug.current == "get-started"][0]{
  landingPageBuilder[] {
    _type == 'industries' => {
      ...,
      "industries": industries[]->title
    }
  }
}

Note the difference: industries[]->title (without curly braces) gives you ["Title 1", "Title 2"], while industries[]->{title} gives you [{title: "Title 1"}, {title: "Title 2"}].

The key takeaway: conditional projections inside array projections need the spread operator to preserve existing fields, otherwise you only get what you explicitly project in the conditional block. Your dereference syntax was correct all along!

Hi Todd. To be sure it’s not overriding your conditional, could you please try moving the
before the
_type == ‘industries’ => {
line?
Ah you are right - it was the spread that was preventing it work! Thanks
user A
Hey
user A
So the spread seems to break a few things – for example this works:
landingPageBuilder[] {
        _type == 'industries' => {
         industries[]->{title,summary,headline,icon},
         ...
       },
       _type == 'organisationalRoles' => {
         roles[]{
           role->,

         },
       }
    }
But this doesn't - so how might I get the rest of the fields in the roles[] array?

landingPageBuilder[] {
        _type == 'industries' => {
         industries[]->{title,summary,headline,icon},
         ...
       },
       _type == 'organisationalRoles' => {
         roles[]{
           ..., // how do I make this work without breaking the role-> reference?
           role->,

         },
         ...,
       }
    }
is this a bug, as by the docs it appears you should be able to use the spread operator to catch everything else (like an 'else' statement) for conditionals?
Hi
user A
I added this as an issue in the GROQ github considering it sounds like a bug of some kind? https://github.com/sanity-io/GROQ/issues/97
hello, could you please try this:

..., 
landingPageBuilder[] {
   ...,
   _type == 'industries' => {
         ...,
         industries[]->{title, summary, headline, icon},
       },
       _type == 'organisationalRoles' => {
         ...,
         roles[]{..,role->},
       }
    }

(and I will answer your additional questions as soon as a get a bit breathing room, had a tight schedule for some pushes 😅 )
Ah that works,
user J
, thank you... But why? The only difference i can see is that the spread operator catch all is before the reference fields, not after?
Its made by people who don’t love chaos 😉jup you basically say: we’re gonna need everything and then do …
ohh, I was thinking of them like 'else' statement - eg, custom functions for these elements, then after that, catch everything else.
Hi Todd. There’s some explanation here that I hope helps.
Thanks
user A
. I'm off for a much needed beer after a long day, but will read through that properly with a fresh mind tomorrow morning 🙂
I noticed your note when you closed the issue, which essentially aligns with that link. Still worth a read, IMO, but in the meantime, enjoy that beer!

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?