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

5 replies
Last updated: Jan 28, 2026
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

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?