GROQ: Access root document title from custom object field

6 replies
Last updated: May 29, 2023
What should my GROQ be to get the root document's title from a custom object's field? I feel like I should be able to just use
[@].title
but that's just returning null.
AI Update

Based on your question, it sounds like you're trying to dereference a reference field within a custom object, but [@].title isn't working. The issue is that @ by itself doesn't dereference - you need to use the reference access operator -> instead.

The correct syntax would be:

[@->].title

Or more commonly written as:

@->.title

The @ symbol in GROQ represents the current element (useful in array mappings or projections), but to actually resolve a reference to get the referenced document's data, you need the -> operator.

Here's how it works: when you have a reference field, it's stored as {"_ref": "document-id"}. The reference access operator (->) performs an internal subquery to fetch the actual document with that _id and replace the reference with the full document data.

Example scenarios:

If you have a single reference field:

*[_type == "myDocument"] {
  myReference->.title
}

If you're already in a projection context using @:

*[_type == "myDocument"] {
  "refTitle": @.myReference->.title
}

If you're working with an array of references:

*[_type == "myDocument"] {
  myReferences[]->title
}

If you're dealing with mixed content (some references, some inline objects), you can use coalesce:

{
  "title": coalesce(@->.title, @.title)
}

If @->.title is still returning null, double-check that:

  1. The field actually contains a reference (has a _ref property)
  2. The referenced document exists and has a title field
  3. You have the correct field path to the reference

Hope this helps!

Show original thread
6 replies
You can traverse up in scope using the parent operator (
^
), and they can be chained together (e.g.,
^.^.title
).
What's the "special variable"
@
for then? I thought that I could access the root document with it?
@
is whatever’s currently in scope.
Maybe this will help because I'm still confused. The following code is from a custom seo object which I've added to my page document schema. I want to get the title from the page document this object is being used on.
// studio/schemas/objects/seo.ts

initialValue: async (_, context) => {
    const client = context.getClient;
    const title = await client({ apiVersion })?.fetch(`[@][].title`);

    return title || '';
},
Sorry, I should clarify.
@
is whatever’s in scope of the current GROQ query, but it can’t be used to establish a query. In this case, the query being provided to
client.fetch
doesn’t provide any context about what it’s after, as the client will not infer the current document and pass it along. Instead, it will usually be provided with an
_id
, which will be used in the query.
Here’s an example that hopefully shows how
^
and
@
work. When you dereference, you are performing a subquery not unlike the implementation of
author
below (so the
author
filter is close to or the same as `author->`—minus the projection with `authorDetails`—but I’ve used this method to demonstrate a point.

*[_type == 'post'][0]{
  'author': *[_type == 'author' && _id == ^.author._ref]{
    'authorDetails': @
  },
  'postDetails': @
}
In the query
*[_type == 'author' && _id == ^.author._ref]
, we want to compare the
_id
that’s in scope (i.e., of the author) to the
author._ref
value of the parent (i.e., the post). The
^
operator lets us do this by looking at the parent scope. This only works because we have a
post
document in the context of this GROQ query. We couldn’t query for
*[_type == 'author']
and be able to use the
^
operator to look any higher than the root.
The
@
in
authorDetails
is returning us the current scope at that point, which is in the context of the
*[_type == 'author' && _id == ^.author._ref]
query.
The
@
in
postDetails
is returning us the current scope at that point, which is in the context of the
*[_type == 'post'][0]
query.
As for your desire to fetch a field on the current document in the
initialValue
callback, I don’t believe that will be possible since at that point, the document doesn’t exist yet. Initial values are only set on document creation, so there won’t be a title to fetch. Hope this helps!
Hey thanks for that detailed response! That makes much more sense now.

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?