Generating a sitemap.xml in NextJS 13 application

Recently, I faced the need to generate a sitemap.xml file for an application built using the NextJS 13 framework. I searched Google but couldn't find a working dynamic generation tutorial for this particular version.

The official NextJS website provides this guide: Crawling and Indexing, but it's based on GetServerSideProps and works only with the framework up to version 12. Fortunately, adapting the instructions for the latest version, NextJS 13.3.0, wasn't too difficult, and I've decided to share the process with you in this article.

To be fair, the part about manually creating the file in the aforementioned guide is still relevant and works with version 13. All you need to do is create a sitemap.xml file in the root of the public directory and manually fill it with content. However, as developers, we're naturally interested in dynamic generation capabilities.

To achieve this, first, create the required route. Inside the app directory, create a sitemap.xml directory. In that directory, create a single file named route.tsx. This file name is conventional for the new NextJS feature called route handlers, which allows you to write custom handlers for NextJS route requests. Essentially, it's the equivalent of API routes in the pages/api directory, but now they live within the app directory and are integrated into the new NextJS router.

From route.tsx, you'll need to export a single function, GET, based on the HTTP request method that the handler should process:

export const GET = async () => {
  return new Response('Hello NextJS', {
    status: 200,
  })
}

As you can see, the method returns an instance of the standard Response class from the Fetch API, which is also one of the advantages of route handlers. The method receives an instance of Request as input, but we're not interested in that, so we can skip it.

Now we just need to adapt the first argument of the Response constructor to our needs:

import api from '@/lib/api'

export const GET = async () => {
  const posts = await api.getPosts()
  const SITE_URL = process.env.NEXT_PUBLIC_APP_HOST
  const xml = `<?xml version="1.0" encoding="UTF-8"?>
  <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
	  <url>
	    <loc>${SITE_URL}</loc>
	  </url>
	  <url>
	    <loc>${SITE_URL}/about</loc>
	  </url>
    ${posts
      .map(({ slug }) => {
        return `
      <url>
          <loc>${`${SITE_URL}/posts/${slug}`}</loc>
      </url>
    `
      })
      .join('')}
  </urlset>
`

  return new Response(xml, {
    status: 200,
    headers: { 'Content-Type': 'text/xml' },
  })
}

At the top of the xml string, we can list the static URLs of your site; in my case, this includes the homepage and /about. Next, we can add all dynamic site pages in a loop. I use the getPosts method of the API class instance, which retrieves all posts from the database.

That's it! Now, Google's crawler will easily find the sitemap.xml file at the root of your site, and you can sleep well knowing your SEO performance is taken care of.

I hope you found this article helpful and engaging. Don't hesitate to explore the NextJS 13 framework further and leverage its powerful features to build amazing web applications. Good luck, and see you in the next article. Take care!