Skip Navigation

Scott Spence

Sitemap Generation for Dynamic Routes In NextJS with the Sanity Client

3 min read
Hey! Thanks for stopping by! Just a word of warning, this post is over 2 years old, . If there's technical information in here it's more than likely out of date.

A sitemap is an important for Search Engine Optimisation (SEO) because it makes it easier for Google and other search engines to find your site’s pages.

Google ranks web pages not just websites. There is no downside of having an XML Sitemap and having one can improve your SEO.

At the beginning of January I asked a question in the Sanity.io Slack.

slack message asking about site map

The Lee Robinson solution didn’t work for me so I reached out on the Sanity slack channel and Knut being the legend he is offered up how they have done it:

knut slack reply

const client = require('../../client')
const sm = require('sitemap')
const defaultUrls = [
  { url: '/', changefreq: 'daily', priority: 1 },
  { url: '/pricing', priority: 0.5 },
  { url: '/pricing/compare', priority: 0.5 },
  { url: '/docs', priority: 0.7 },
  { url: '/community', priority: 0.7 },
  { url: '/blog/', changefreq: 'weekly', priority: 0.7 },
]
async function getSitemap() {
  const { routes, blogposts } = await client.fetch(`
  {
    "routes": *[_type == "route" && includeInSitemap],
    "blogposts": *[_type == 'post' && includeInSitemap == true && publishedAt < $now] | order(publishedAt desc) {
      slug
    }
  }
  `)
  const urls = routes
    .filter(({ slug = {} }) => slug.current)
    .reduce(
      (acc, route) => [
        ...acc,
        {
          url: route.slug.current,
          priority: route.sitemapPriority || 0.5,
        },
      ],
      defaultUrls
    )
  const blogUrls = blogposts
    .filter(({ slug = {} }) => slug.current)
    .map(post => {
      return {
        url: `/blog/${post.slug.current}`,
        priority: 0.5,
      }
    })
  return sm.createSitemap({
    hostname: 'https://www.sanity.io',
    cacheTime: 600000,
    urls: urls.concat(blogUrls),
  })
}
module.exports = function sitemapXML(req, res, next) {
  res.setHeader('Content-Type', 'application/xml')
  getSitemap()
    .then(result => {
      res.send(result.toString())
    })
    .catch(next)
}

I was just about to start getting my head around how that was done then James Weis came in with setting the headers to text/xml

james weis reply headers to text/xml

This made a lot more sense to me so I implemented this straight away.

Create the file as pages/sitemap.xml.js then the following:

import groq from 'groq'
import sanityClient from '../sanity-client'

export default function SiteMap() {
  return <div>loading</div>
}

export async function getServerSideProps({ res }) {
  const baseUrl = `https://myawesomesite.com`
  const query = groq`{
      "countries": *[_type == 'country']{slug},
    }`
  const urls = await sanityClient.fetch(query)
  const countries = urls.countries.map(page => {
    const slug =
      page.slug.current === '/' ? '/' : `/${page.slug.current}`
    return `
      <loc>${baseUrl}${slug}</loc>
      <changefreq>daily</changefreq>
      <priority>0.7</priority>
    `
  })

  const locations = [...countries]
  const createSitemap = () => `<?xml version="1.0" encoding="UTF-8"?>
    <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
        ${locations
          .map(location => {
            return `<url>
                      ${location}
                    </url>
                  `
          })
          .join('')}
    </urlset>
    `
  res.setHeader('Content-Type', 'text/xml')
  res.write(createSitemap())
  res.end()
  return {
    props: {},
  }
}

This is genius!

So what the code block there is doing is saving the file as the sitemap and it’s located with the rest of the pages as sitemap.xml, this can them be added to the Google search console as the sitemap and located as https://myawesomesite.com/sitemap.xml.

I asked for permission to document this from James and he was happy with being mentioned, thanks James. 🙏

asking for permission to add this content to my blog

There's a reactions leaderboard you can check out too.

Analytics Information

Daily analytics for this post

Views Today
4
Sep 28, 2023 2:41 PM
Visitors Today
2
Sep 28, 2023 2:41 PM
Entries Today
2
Sep 28, 2023 2:41 PM

Monthly analytics for this post

Views This Month
77
Sep 1, 2023 - Sep 28, 2023
Visitors This Month
42
Sep 1, 2023 - Sep 28, 2023
Entries This Month
46
Sep 1, 2023 - Sep 28, 2023

Yearly analytics for this post

Views This Year
1.3k
Jan 1, 2023 - Sep 28, 2023
Visitors This Year
699
Jan 1, 2023 - Sep 28, 2023
Entries This Year
746
Jan 1, 2023 - Sep 28, 2023

Copyright © 2017 - 2023 - All rights reserved Scott Spence