Querying a custom type reference in Sanity using groq
You can absolutely dereference (expand) those references directly in your GROQ query! The key is using the -> operator, which tells GROQ to follow the reference and pull in the actual document data.
For your specific case with references inside a body array (portable text), you'll want to use array projection to selectively expand only the reference items. Here's how to modify your query:
*[_type == "post" && slug.current == $slug][0] {
_id,
title,
body[] {
...,
_type == "jccClass" => @->
},
"slug": slug.current
}This syntax works by:
body[]- iterates over each item in the body array...- spreads all the existing properties (keeps blocks, images, etc. intact)_type == "jccClass" => @->- conditionally dereferences: when the item type is "jccClass", the@refers to the current item and->follows the reference
If you want to control which fields from the referenced jccClass document get returned (recommended for performance), you can be more specific:
*[_type == "post" && slug.current == $slug][0] {
_id,
title,
body[] {
...,
_type == "jccClass" => @-> {
_id,
title,
description,
// whatever fields you need from jccClass
}
},
"slug": slug.current
}This approach is much more efficient than having your component fetch each reference separately, as it resolves everything in a single query. The Content Lake will handle all the dereferencing server-side.
The -> operator works for any reference type in Sanity, and the conditional projection pattern (_type == "something" => ...) is the standard way to handle mixed arrays like portable text where you have blocks, images, and custom reference types all together.
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.