How to Create a Dynamic Sitemap using NextJS & Sanity

Updated at: 17 Feb 2024Deepak Painkra

Have multiple dynamic pages and get confused about how to create a sitemap using Nextjs and the sanity backend, do not worry. I will show you how to create a dynamic sitemap using server-side props in NextJS, which is also the best practice for SEO.

The reason why I'm sticking with Pages Router is that most of the library and hosting platforms support Pages Router, and if you want to host your app on Vercel, you can think of using App Router, and if you want to host anywhere else, then App Router will be not suitable, Even pages router not getting supported by most of the hosting platform. Also, the App Router has performance issues.

 

Site Map Setup,

Make sure to add this field to the sanity schema folder, and for this tutorial, we have schemas such as mobile.js, computer.js and tech.js

fields: [
    defineField({
      name: 'lastModified',
      title: 'Last Modified',
      type: 'datetime',
    }),   
    defineField({
      name: 'slug',
      title: 'Slug',
      type: 'slug',
      options: {
        source: 'title',
        maxLength: 96,
      },
    }),
]

I'm assuming you have already set up your NextJS and Sanity project, so let's get started,

First, setup your environment variable file in the root directory

NEXT_PUBLIC_SANITY_PROJECT_ID=your id
NEXT_PUBLIC_SANITY_DATASET=your production id
NEXT_PUBLIC_SANITY_API_VERSION=your date

Then for best practices, create a folder name utils in the root directory of the NextJS project. After that, we will create a dataset.js file inside the utils folder,

write this code inside the dataset.js

import { createClient } from 'next-sanity'

const client = createClient({
    projectId: process.env.NEXT_PUBLIC_SANITY_PROJECT_ID,
    dataset: process.env.NEXT_PUBLIC_SANITY_DATASET,
    apiVersion: process.env.NEXT_PUBLIC_SANITY_API_VERSION,
    useCdn: false

  });

export async function getAllMobile() {
  const query = '*[_type == "mobile"]{ slug, lastModified }';
  return await client.fetch(query);
}

export async function getAllComputer() {
  const query = '*[_type == "computer"]{ slug, lastModified}';
  return await client.fetch(query);
}

export async function getAllTech() {
  const query = '*[_type == "tech"]{ slug, lastModified }';
  return await client.fetch(query);
}

First, we need to setup our sanity client, then after we will create three functions to get a slug and the last modified date,

My category is mobile, computer and tech.

 

Sitemap For Single Dynamic Route and Static Pages,

It will be the most sufficient ways to create a sitemap for NextJS and Sanity backend, and single dynamic pages or schema means it can use fewer resources, and you could save your API request.

It will be the same, but this time, I have also added the static pages and single dynamic pages, so let's write the code,

// pages/sitemap.xml.js
import { getAllMobile} from '../utils/dataset.js';



const generateSitemapXml = (staticData, dynamicData) => {
  let sitemap = `<?xml version="1.0" encoding="UTF-8"?>\n`;
  sitemap += `<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">\n`;

  staticData.forEach((page) => {
    sitemap += `  <url>\n`;
    sitemap += `    <loc>${`${process.env.SITE_URL}${page.url}`}</loc>\n`;
    sitemap += `    <lastmod>${page.lastModified}</lastmod>\n`;
    sitemap += `  </url>\n`;
  });

  dynamicData.forEach((item) => {
    sitemap += `  <url>\n`;
    sitemap += `    <loc>${`${process.env.SITE_URL}/techblog/${item.slug.current}`}</loc>\n`; 
    sitemap += `    <lastmod>${new Date(item.lastModified).toISOString()}</lastmod>\n`;
    sitemap += `  </url>\n`;

  });

  sitemap += `</urlset>\n`;

  return sitemap;
};

const Sitemap = () => {};

export async function getServerSideProps({ res }) {
  const staticPages = [
    { url: '/', lastModified: '2023-08-09' },
    { url: '/mobile', lastModified: '2023-08-09' },
    { url: '/computer', lastModified: '2023-08-09' },
    { url: '/tech', lastModified: '2023-08-13' },
    { url: '/contact', lastModified: '2023-08-09' },
  ];

  const mobileData = await getAllMobile(); 
 

  const dynamicData = [...mobileData];

  const sitemapXml = generateSitemapXml(staticPages, dynamicData);

  res.setHeader('Content-Type', 'text/xml');
  res.write(sitemapXml);
  res.end();

  return {
    props: {},
  };
}

export default Sitemap;

A dataset.js will be the same. You have to remove two of the dynamic routes, and this will be code for a single dynamic and static page,

 

Sitemap For Mutiple Dynamic Pages

Now next step is to create a sitemap.xml.js file inside the pages directory,

First, we will import our function from the utils directory, and we are using server-side proper for data fetching,

After that, we must create three constants to get all the slugs, such as mobiles, computers, and techs.

To show a sitemap on a single page, we must put it inside the array.

// pages/sitemap.xml.js
import { getAllComputer, getAllMobile, getAllTech} from '../utils/dataset.js';

const generateSitemapXml = (data) => {
  let sitemap = `<?xml version="1.0" encoding="UTF-8"?>\n`;
  sitemap += `<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">\n`;

  data.forEach((item) => {
    sitemap += `  <url>\n`;
    sitemap += `    <loc>${`https://www.example.com/${item.slug.current}`}</loc>\n`; // Replace 'your-domain.com' with your actual domain.
    sitemap += `    <lastmod>${new Date(item.lastModified).toISOString()}</lastmod>\n`;
    sitemap += `  </url>\n`;
  });

  sitemap += `</urlset>\n`;

  return sitemap;
};

const Sitemap = () => {};

export async function getServerSideProps({ res }) {
  const mobiles = await getAllMobile();
  const computers = await getAllComputer();
  const techs = await getAllTech();

  const allData = [...mobiles, ...computers, ...techs];
  const sitemapXml = generateSitemapXml(allData);

  res.setHeader('Content-Type', 'text/xml');
  res.write(sitemapXml);
  res.end();

  return {
    props: {},
  };
}

export default Sitemap;

I have taken reference from NextJS Official Documentation.

After that, we will pass the parameter. A data is an array of datasets which extracts the slug and last modified date.