How to get parent document slugs in nested Sanity structure?

6 replies
Last updated: Jan 26, 2022
Is there a method in the Studio to get the slugs from all parent documents?
I have a nested structure of
/:module/:chapter/:section
and I’d like to get the chapter and module slugs when fetching the section
AI Update

Based on what you're describing, you have a hierarchical structure where sections reference chapters, and chapters reference modules. There isn't a built-in "get all parent documents" method in Sanity Studio, but you can absolutely achieve this using GROQ queries with reference dereferencing.

Schema Setup

First, make sure your schema uses reference fields to establish the parent relationships:

// section.js
{
  name: 'section',
  type: 'document',
  fields: [
    {
      name: 'slug',
      type: 'slug'
    },
    {
      name: 'chapter',
      type: 'reference',
      to: [{type: 'chapter'}]
    }
  ]
}

// chapter.js
{
  name: 'chapter',
  type: 'document',
  fields: [
    {
      name: 'slug',
      type: 'slug'
    },
    {
      name: 'module',
      type: 'reference',
      to: [{type: 'module'}]
    }
  ]
}

Fetching Parent Slugs

When querying for a section, you can use GROQ's -> operator to follow references and get the parent slugs:

*[_type == "section" && slug.current == $sectionSlug][0] {
  slug,
  "chapterSlug": chapter->slug.current,
  "moduleSlug": chapter->module->slug.current
}

This query:

  • Finds your section by its slug
  • Follows the chapter reference with -> to get the chapter's slug
  • Chains another -> to follow the chapter's module reference

You can also get the full parent objects if you need more than just slugs:

*[_type == "section" && slug.current == $sectionSlug][0] {
  slug,
  chapter-> {
    slug,
    module-> {
      slug
    }
  }
}

In Studio Context

If you need to access this information within the Studio (for example, in a custom component or plugin), you can use the Sanity client to run these queries:

import {useClient} from 'sanity'

const client = useClient({apiVersion: '2024-01-01'})

const result = await client.fetch(
  `*[_type == "section" && _id == $id][0] {
    slug,
    "chapterSlug": chapter->slug.current,
    "moduleSlug": chapter->module->slug.current
  }`,
  {id: documentId}
)

The key insight from the reference fields documentation is that references in Sanity are bidirectional when queried, meaning you can traverse them efficiently in either direction using GROQ's dereferencing operator (->).

This approach gives you full control over which parent information you need and avoids storing redundant slug data in your documents, keeping your content normalized and easier to maintain.

Show original thread
6 replies
I think the best approach would be to create a custom slugify function that does this for you. There's not a neat and tidy way to do it, though. If it's set up that the section references a chapter, which then references a module, the following will work:
*Note: you will need to install slugify and import the sanity client for this to work.


{
      name: 'slugWithPath',
      title: 'Slug with Path',
      group: 'community',
      options: {
        source: '_id',
        slugify: async (input) => {
          const query = `*[_id match $id][0]{ 
            title,
            'chapterSlug': chapter->.slug.current,
            'moduleSlug': chapter->.module->.slug.current
          }`
          const document = await sanityClient
            .fetch(query, { id: `**${input}`})
          
          const {
            title,
            chapterSlug,
            moduleSlug
          } = document
          
          return `${moduleSlug}/${chapterSlug}/${slugify(title, { lower: true })}`
        }
      }
    }
Thanks
user M
, I’ll give this a try
Just for fun, you could also assemble the path with GROQ:

*[ _id == "section" ][0] {
  ...*[_type == "chapter" && references(^._id)][0]
  {
    "slug": *[_type == "module" && references(^._id)][0].slug + '/' + slug + '/' + ^.slug
  },
}

https://groq.dev/SRHr6fRV7Q1gxX9BuHxN7i
Thanks
user G
Your code samples have helped me realize how powerful GROQ is
That query as part of
user M
’s slugify code is probably exactly what I need
One note for anyone else looking to use something like this — it needs to reference
slug.current
rather than just
slug
— This gets the full route for my schema setup
*[ _type == "section" ][0] {
  ...*[_type == "chapter" && references(^._id)][0]
  {
    "slug": *[_type == "module" && references(^._id)][0].slug.current + '/' + slug.current + '/' + ^.slug.current
  },
}

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?