Recommended content model for nested geographic URL hierarchy (country/state/county/city)

5 replies
Last updated: Feb 11, 2022
Hello, I'm curious what would be the most recommended content model for nested documents/pages? For example, lets say I have a site that goes https://example.com/country/state/county/city , which then ends up being https://example.com/usa/georgia/fulton/atlanta . A user can also view content lower in the tree like https://example.com/usa/georgia , for example.
I initially thought of making a document type for each path (i.e.,
country
,
state
, etc.) and then creating a hierarchical structure by referencing them together under a singleton document . I've looked at the docs about connected content , but I'm still trying to figure out how to use references for this particular use case. Thanks!
AI Update

Great question! For your hierarchical URL structure like country/state/county/city, you're on the right track thinking about references. Let me walk you through the most recommended approaches.

The Reference-Based Approach

For your hierarchy, the most flexible approach is to create separate document types for each level and connect them with reference fields. You have two main options:

Each document references its parent:

// city.js
{
  name: 'city',
  type: 'document',
  fields: [
    {name: 'name', type: 'string'},
    {name: 'slug', type: 'slug'},
    {
      name: 'county',
      type: 'reference',
      to: [{type: 'county'}]
    }
  ]
}

// county.js references state, state references country, etc.

Pros:

  • Reference field appears where editors naturally expect it (in the child document)
  • Easy to create new cities and assign them to counties
  • Simpler editorial workflow

2. Top-Down References

Each document contains arrays of references to its children:

// country.js
{
  name: 'country',
  type: 'document',
  fields: [
    {name: 'name', type: 'string'},
    {name: 'slug', type: 'slug'},
    {
      name: 'states',
      type: 'array',
      of: [{type: 'reference', to: [{type: 'state'}]}]
    }
  ]
}

Pros:

  • Easier to see the full hierarchy from the top
  • Good if you need to control order of children

Building Dynamic URLs

Regardless of which direction you choose, you can build full paths using GROQ queries. For a bottom-up structure:

*[_type == "city" && slug.current == $slug][0] {
  name,
  "path": slug.current,
  county-> {
    name,
    "path": slug.current,
    state-> {
      name,
      "path": slug.current,
      country-> {
        name,
        "path": slug.current
      }
    }
  }
}

The -> operator follows references, and because references are bidirectional when queried, you can traverse them from either direction.

Important Consideration

An important thing to know: references are only visible in the Studio UI where the reference field is defined. So if you use bottom-up references, the city document will show the county reference field, but the county document won't show which cities reference it in the UI (though you can still query this relationship using the references() function).

Skip the Singleton

You mentioned using a singleton document to manage the hierarchy. This is usually unnecessary and adds complexity. The reference structure itself creates the hierarchy, and you can query it dynamically. A singleton would only be useful if you need to manually curate or order the hierarchy in a specific way.

However, if you need to create navigation menus that differ from your content structure, the Table of Contents pattern can be useful. This lets you provide different hierarchical views for different audiences without changing your core content structure.

Alternative: Path-Based Slugs

For simpler cases, you could also store the full path in each document's slug (e.g., usa-georgia-fulton-atlanta) and use string manipulation in your frontend. This is less flexible but might work if your hierarchy is relatively flat and doesn't change often.

The reference-based approach gives you the most flexibility and aligns well with Sanity's graph-like content organization, making it the recommended pattern for your use case!

Show original thread
5 replies
Hey User! You could handle this through reference fields. For example, a city would reference a state which would then reference a country. You can then use GROQ on your frontend to dynamically build out the paths. Alternatively, you could use the method I described in this thread to build out the slugs in your document.
Thanks, User! This looks great! Quick question, are there any pros or cons between referencing from bottom to top (
city
references a
state
etc.) vs. top to bottom (
country
references a
state
and so on)?
References are bi-directional when you query them so, in that sense, it doesn't matter where you place it. However when it comes to the Studio UI, you'll only see the reference field in the document where you set the reference. To clarify: if you set a reference to a country in a city document, the city document would show that relationship not the country document. I'd say place it wherever will be most intuitive for your editors to look for it. Does that make sense?
Ahhh, I see. Thanks for clarifying. That makes sense. I guess this goes back to how User described how the bi-directional querying works. I think I know what to do now. Thanks again!
You're welcome!

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?