Lauro Silva
Lauro Silva
4 min read
Creating a Dynamic Sitemap with Next.js 13+ and Sanity icon

Creating a Dynamic Sitemap with Next.js 13+ and Sanity

A comprehensive guide on implementing a dynamic sitemap in Next.js 13+ using data from Sanity CMS to improve SEO.

On this page

Creating a sitemap is crucial for better SEO and ensuring that search engines can efficiently crawl and index your website's pages. In this guide, we'll create a dynamic sitemap using data fetched from Sanity in a Next.js 13+ application with the app directory.

#Prerequisites

  • A basic understanding of Next.js framework
  • Sanity configured and running with some content
  • Node.js and npm installed

#Set Up Your Next.js Project

If you don't already have a Next.js project set up, you can create one using the following command:

npx create-next-app my-nextjs-app --use-npm
cd my-nextjs-app

#Configure Sanity Client

Install Sanity client if you haven't already:

npm install @sanity/client

Create a client.ts file to configure your Sanity client in your lib directory:

import sanityClient from '@sanity/client'

export const client = sanityClient({
	projectId: 'your-project-id', // replace with your project ID
	dataset: 'your-dataset-name', // replace with your dataset name
	useCdn: true, // Use CDN for performance
	apiVersion: '2023-01-01' // Use current date or specific API version
})

#Fetch Data from Sanity

Define fetching functions to retrieve data from Sanity.

Create a routes.ts file in your lib directory:

import {groq} from 'next-sanity'
import {client} from './client'

// Simplified function to fetch article slugs
export const getAllArticles = async (): Promise<
	{slug: string; lastModified: string}[]
> => {
	const articlesQuery = groq`*[_type == "article"]{ "slug": slug.current, _updatedAt }`
	const articles =
		await client.fetch<{slug: string; _updatedAt: string}[]>(
			articlesQuery
		)
	return articles.map((article) => ({
		slug: `/articles/${article.slug}`,
		lastModified: article._updatedAt
	}))
}

// Simplified function to fetch software library slugs
export const getAllSoftwareLibraries = async (): Promise<
	{slug: string; lastModified: string}[]
> => {
	const librariesQuery = groq`*[_type == "software-library"]{ "slug": slug.current, _updatedAt }`
	const libraries =
		await client.fetch<{slug: string; _updatedAt: string}[]>(
			librariesQuery
		)
	return libraries.map((library) => ({
		slug: `/libraries/${library.slug}`,
		lastModified: library._updatedAt
	}))
}

// Fetch all routes for the sitemap
export const getAllRoutes = async () => {
	const articleRoutes = await getAllArticles()
	const libraryRoutes = await getAllSoftwareLibraries()

	// Add any required static routes manually
	const staticRoutes = [
		{
			slug: '/',
			lastModified: new Date().toISOString().split('T')[0]
		},
		{
			slug: '/about',
			lastModified: new Date().toISOString().split('T')[0]
		},
		{
			slug: '/contact',
			lastModified: new Date().toISOString().split('T')[0]
		},
		{
			slug: '/articles',
			lastModified: new Date().toISOString().split('T')[0]
		},
		{
			slug: '/newsletter',
			lastModified: new Date().toISOString().split('T')[0]
		},
		{
			slug: '/workshops',
			lastModified: new Date().toISOString().split('T')[0]
		}
	]

	return [...staticRoutes, ...articleRoutes, ...libraryRoutes]
}

#Generate Sitemap Using Metadata API

In Next.js 13+, you leverage the new app directory structure to create and serve the sitemap.

Create a file named sitemap.ts in your app directory:

import {getAllRoutes} from '../lib/routes'

export default async function sitemap() {
	const routes = await getAllRoutes()
	const baseUrl =
		process.env.NEXT_PUBLIC_SITE_URL ||
		'https://your-website.com'

	const urls = routes.map(({slug, lastModified}) => ({
		url: `${baseUrl}${slug}`,
		lastModified
	}))

	return urls
}

#Create Robots.txt (Optional but Recommended)

To provide instructions to web crawlers, you can create a robots.txt file.

Create a file named robots.ts in your app directory:

const siteUrl =
	process.env.NEXT_PUBLIC_SITE_URL || 'https://your-website.com'

export default function robots() {
	return {
		rules: [
			{
				userAgent: '*', // Applies to all user agents
				allow: '/' // Allows access to the entire site
			}
		],
		sitemap: `${siteUrl}/sitemap.xml`,
		host: siteUrl
	}
}

#Configure Environment Variables

Ensure you have configured the NEXT_PUBLIC_SITE_URL in your .env file:

NEXT_PUBLIC_SITE_URL=https://your-website.com

#Testing

  1. Run Your Next.js App:
npm run dev
  1. Visit Your Sitemap URL:
http://localhost:3002/sitemap.xml
  1. Visit Your Robots URL (if implemented):
http://localhost:3002/robots.txt

By following these steps, you'll have a dynamic sitemap generated and served by your Next.js application, utilizing data fetched from Sanity. This setup will enhance your website's SEO and ensure that your dynamic content is efficiently indexed by search engines.

#Final Directory Structure

apps/
└── lauro-silva/
    ├── app/
       ├── sitemap.ts
       ├── robots.ts (optional)
    └── sanity/
        ├── lib/
           ├── client.ts
           ├── routes.ts
// Other necessary files and directories

This approach fully utilizes the Next.js 13+ app directory and the new metadata API, ensuring a clean and performant implementation for creating dynamic sitemaps and potentially a robots.txt file for your website.