How to model a hierarchical tree-structure taxonomy in Sanity?

8 replies
Last updated: Mar 30, 2021
Hi all!
New to Sanity
🙂
How would I model a tree-structure taxonomy of pre-defined values?

I can make a simple one-level taxonomy with an array of strings, with the strings pre-defined.

But I want to make a tree-structure where you can choose the whole category (parent), or a child, or even a grand-child. Is this possible?

My use case is Norwegian counties and municipalities, where I want the user to be able to select:

• The whole country
• Any number of specific counties, say Viken
• Any number of specific sub-counties / municipalities, say Fredrikstad (all of these belong to a county)
Any leads on how to do this easily?
AI Update

Great question! For modeling Norwegian counties and municipalities with flexible selection options, you'll want to use reference fields with parent-child relationships. Here's how to set this up:

Basic Schema Structure

Create a location document type where each location can optionally reference a parent:

// schemas/location.js
import {defineField, defineType} from 'sanity'

export default defineType({
  name: 'location',
  title: 'Location',
  type: 'document',
  fields: [
    defineField({
      name: 'title', 
      type: 'string',
      title: 'Name'
    }),
    defineField({
      name: 'parent',
      type: 'reference',
      to: [{type: 'location'}],
      // This ensures we can only select parent locations (not children)
      options: {
        filter: '!defined(parent)',
      },
    }),
  ],
})

This gives you:

  • Country level: Documents with no parent (Norway)
  • County level: Documents with country as parent (Viken)
  • Municipality level: Documents with county as parent (Fredrikstad)

How to Use in Other Documents

When you want users to select locations (say, in a post or event document), you can allow them to select any level of the hierarchy:

defineField({
  name: 'locations',
  type: 'array',
  of: [{
    type: 'reference',
    to: [{type: 'location'}]
  }]
})

Or restrict to only specific levels (like only municipalities):

defineField({
  name: 'municipality',
  type: 'reference',
  to: [{type: 'location'}],
  options: {
    filter: 'defined(parent)' // Only show locations that have a parent
  }
})

Querying the Hierarchy

When querying, you can "follow" the references up the tree:

*[_type == "post"]{
  title,
  locations[]->{
    title,
    parent->{
      title,
      parent->
    }
  }
}

Studio Setup with Structure Builder

For a better editing experience, check out the Creating a Parent/Child Taxonomy guide. It shows how to:

  • Set up filtered document lists for each parent
  • Use initial value templates to pre-fill the parent field
  • Create a nice hierarchical view in the Studio

The key insight: references in Sanity are bidirectional when queried, but only appear in the UI where they're defined. So even though you put the parent field on the child document, you can query in both directions!

Show original thread
8 replies
Later I will want to filter on these, so that I can say "Show me all content in the county Viken" and it'll know that all the documents marked with the sub-county "Fredrikstad" are also in Viken.
I guess at least the “tree selection” part could be with a custom input component?
Basically I want it to function like the finn.no area selector:
Found a sort of solution, though it doesn’t feel like the most elegant one
Proof of concept code:

const viken = {
  name: 'vikenSelector',
  title: 'Viken',
  type: 'object',
  options: {
    collapsible: true,
    collapsed: true,
  },
  fields: [
    {
      title: 'Hvor i Viken?',
      name: 'vikenOverview',
      type: 'array',
      options: {
        list: [
          { title: "Hele viken", value: "viken" }, 
          { title: "Fredrikstad", value: "fredrikstad" }, 
          { title: "Moss", value: "moss" } 
        ]
      },
      of: [
        {
          type: 'string'
        }
      ]
    }
  ]
}

const fylkeTest = {
  name: 'where',
  title: 'Hvor?',
  type: 'object',
  fields: [
    viken
  ]
}
Ugly on output though:
Hey User! I think conditional fields might help you clean this up a bit. Here’s a blob with the basics of one.
Thank you User!

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?