Nested navigation structure

By Knut Melvær

Recursive schema for nested navigations

Section

// navigationSection.js
export default {
  name: 'navigation.section',
  type: 'object',
  title: 'Section',
  fields: [
    {
      type: 'reference',
      name: 'target',
      title: 'Target article',
      to: [{ type: 'article' }],
      // _weak: true // enable if you don't want reference integrity checks
    },
    {
      type: 'string',
      name: 'title',
      title: 'Title',
    },
    {
      type: 'array',
      name: 'links',
      title: 'Links',
      of: [{ type: 'navigation.link' }],
    },
  ],
}

Link

// navigationLink.js
export default {
  name: 'navigation.link',
  type: 'object',
  title: 'Link',
  preview: {
    select: {
      title: 'title',
      targetTitle: 'target.title',
    },
    prepare: ({ title, targetTitle }) => ({
      title: title || targetTitle,
    }),
  },
  fields: [
    {
      type: 'reference',
      name: 'target',
      title: 'Target article',
      to: [{ type: 'article' }],
      description: 'No target article turns the item into a subheading.',
      // _weak: true // enable if you don't want reference integrity checks  
    },
    {
      type: 'string',
      name: 'title',
      title: 'Title',
      description: 'Override title from the target article.',
    },
    {
      type: 'array',
      name: 'children',
      title: 'Children',
      of: [{ type: 'navigation.link' }],
    },
  ],
}

Navigation

// navigation.js
export default {
  name: 'navigation',
  type: 'document',
  title: 'Navigation',
  fields: [
    {
      type: 'string',
      name: 'name',
      title: 'Name',
    },
    {
      type: 'string',
      name: 'title',
      title: 'Title',
    },
    {
      type: 'array',
      name: 'sections',
      title: 'Sections',
      of: [{ type: 'navigation.section' }],
    },
  ],
};

Querying the data

// getNavigationTree.js

/*
 * Only fetches structures 2 levels down.
 * You can extend it by repeating the pattern inside this query
 */
import client from './sanityClient'

const navigationTree = client.fetch(`//groq
       *[_id == 'mainNavigation'][0] {
        ...,
        sections[]{
          ...,
          target->{title, slug, _id},
          links[]{
            ...,
            target->{title, slug, _id},
            children[]{
              ...,
              target->{title, slug, _id}
            }
          }
        }
      }
      `)
    
export default navigationTree

This schema for nested navigation allows for a separation of content and presentation. Not only can you connect the same pieces of content to different navigation structures, but you also gain from establishing reference integrity that warns or prevents you from deleting content that's represented in navigation.

Contributor

Other schemas by author