Sanity Pioneers: Get early access to betas, extra AI credits, and a direct line to the engineering team. Apply now

How to fetch all nested references dynamically with one GROQ query?

7 repliesLast updated: Nov 30, 2025

Hi all.
I have a GROQ question combined with “how would you solve it” and “possible ways to solve it”.
Tried search but couldn’t find anything on it.

I’m looking for a way to get all references and all its data down the line with one GROQ query. The thing that makes it hard for me is that I have structured it in a quite dynamic way for the editor. So I’m never sure what to expect.

This query


_*[slug.current == "_${queryParameter}"_]_

gets me the page and its fields but the interesting part is this below, it’s a content area with different blocks. To break it down menu has its reference and that reference has it references and so on.


{
  "_key": "c12345",
  "_type": "uiHero",
  "image": {
    "_type": "image",
    "alt": "deer",
    "asset": {
      "_ref": "image-png",
      "_type": "reference"
    }
  },
  "size": "medium"
}{
  "_key": "abc",
  "_type": "uiMenu",
  "description": "Här har vi en lagom beskrivning för denna menyn",
  "menu": {
    "_ref": "<tel:1234-1234-1234|1234-1234-1234>",
    "_type": "reference"
  },
  "title": "Nya menyn"
}
other examples
• page (doucment) &gt; section (object) &gt; grid (object) &gt; information-card (object)
• page (document) &gt; section (object) &gt; menu (document) &gt; menu-category (document) &gt; menu-items (document) &gt; menu-tags (document)
Is it possible to get what I want with one dynamic query or do I need to re-think? How would you do it?

Please let me know if I need to complement with schema structures.

Thanks
🙂
AI Update

Your question touches on a common challenge with GROQ - dereferencing nested references when you have a dynamic content structure. The good news is that GROQ's dereference operator (->) can handle nested references, but you need to chain them explicitly.

Single Query with Nested Dereferencing

You can dereference nested references by chaining the -> operator at each level:

*[slug.current == $queryParameter][0] {
  ...,
  contentArea[] {
    ...,
    // Dereference first level
    menu->,
    // For deeper nesting, chain the operator
    menu-> {
      ...,
      menuCategory->,
      menuCategory-> {
        ...,
        menuItems[]-> {
          ...,
          menuTags[]->
        }
      }
    },
    // Handle image assets
    image {
      ...,
      asset->
    }
  }
}

The Dynamic Content Challenge

Since you mentioned your structure is "quite dynamic" with varying block types, you'll want to handle different _type values. Here's a more robust approach:

*[slug.current == $queryParameter][0] {
  ...,
  contentArea[] {
    ...,
    _type == "uiMenu" => {
      ...,
      menu-> {
        ...,
        categories[]-> {
          ...,
          items[]-> {
            ...,
            tags[]->
          }
        }
      }
    },
    _type == "uiHero" => {
      ...,
      image {
        ...,
        asset->
      }
    },
    // Add other block types as needed
  }
}

Important Limitations

GROQ doesn't support truly recursive or dynamic dereferencing - you need to know the depth and structure ahead of time. Each -> operator performs an internal subquery, so:

Alternative Approaches

If your structure is too dynamic or too deeply nested, consider:

1. Multiple Queries (Client-side)

Fetch the initial page, then make targeted queries for specific references you discover. This gives you more flexibility but requires multiple round trips.

2. Restructure Your Schema

If you're hitting GROQ limitations, it might signal that your content model could be flattened. For example:

3. Use Projections with select()

For truly dynamic content where you don't know what types to expect, you can use conditional projections:

contentArea[] {
  ...,
  select(
    defined(menu) => {
      "menu": menu-> {
        ...,
        categories[]->
      }
    },
    defined(image) => {
      "image": image.asset->
    }
  )
}

4. Handle Mixed Arrays with coalesce()

If you have arrays with both references and inline objects, you can handle them dynamically:

contentArea[]{
  ...,
  "resolvedContent": coalesce(
    @->,  // Resolve if it's a reference
    @     // Return as-is if it's already an object
  )
}

My Recommendation

For your use case, I'd suggest:

The explicit approach may feel verbose, but it's more performant and maintainable than trying to create a "universal" query that handles infinite nesting. GROQ is designed for you to be explicit about what you're fetching, which helps with both performance and predictability.

Would love to see your schema structure if you want more specific advice on optimization!

Show original thread
7 replies

Was this answer helpful?

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.

Related contributions