Stop creating sitemaps manually and use a generator

on April 22, 2024

Creating a sitemap manually can be a tedious task. Learn how to automate the process with a sitemap generator.

Why would you even need a sitemap?

A sitemap is a file that lists all the pages of your website. It helps search engines like Google to understand the structure of your site and index it more effectively. So in short: it helps your site to be found by more people.

The structure of a sitemap

A sitemap is an XML file that follows a specific structure. Here is an example of a simple sitemap:

<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
  <url>
    <loc>https://www.example.com/foo.html</loc>
    <lastmod>2022-06-04</lastmod>
  </url>
</urlset>

The file should always contain the xml declaration as the first line. The <urlset> tag is the root element of the sitemap and contains one or more <url> elements. It also has an attribute xmlns which defines the sitemap schema (in this case 0.9).

The problem with creating a sitemap manually

Creating a sitemap manually can be a tedious task, especially for larger websites. You have to keep track of all the pages and their URLs, and you need to update the sitemap whenever you add or remove pages.

When you are creating a blog like this one for example, you might have to update the sitemap every time you publish a new post. This can quickly become a hassle.

If you forget to update the sitemap, search engines might not index your new pages, which can hurt your site’s visibility in search results.

Or worse, you might accidentally make a mistake in the sitemap, which could lead to the sitemap being invalid and search engines ignoring it altogether, thus creating a negative impact on your previously indexed pages.

Automating the process with a sitemap generator

To avoid these problems, you can easily write a sitemap generator, which creates all entries on demand. This way, you don’t have to worry about updating the sitemap manually anymore.

We’ll be using the library fast-xml-parser which also includes a XML builder.

# I'm using pnpm, but you can also use npm or yarn or bun or whatever you like
pnpm add -D fast-xml-parser

First, you need to create a new folder in your routes directory /src/routes/sitemap.xml/ (yes, it is a folder, not a file).

Inside this folder, create a new file +server.ts, which will serve all requests to the sitemap.

We’ll create a GET endpoint which will return the sitemap as XML.

import { text } from '@sveltejs/kit';
import { XMLBuilder } from 'fast-xml-parser';

export const GET = async () => {
	// Create a raw sitemap object without any entries
	const sitemap = {
		// This is the XML declaration, '?' at the start also adds a ? to the end of the tag
		'?xml': {
			// This weird syntax creates the attributes of the XML declaration
			// e.g. '@_version': '1.0' => <?xml version="1.0" ... ?>
			'@_version': '1.0',
			'@_encoding': 'UTF-8'
		},
		urlset: {
			// Again we create an attribute for the urlset tag using @_
			'@_xmlns': 'http://www.sitemaps.org/schemas/sitemap/0.9'
		}
	};

	// Create a new XML Builder instance for generating the XML file
	const xmlBuilder = new XMLBuilder({
		// This will make sure, the XML is formatted nicely (we can also set it to false)
		format: true,

		// Setting this to false is required since otherwise,
		// not only would the attributes be ignored, but also the
		// attributes would be added as child nodes, which would break the sitemap
		ignoreAttributes: false
	});

	const sitemapXml = xmlBuilder.build(sitemap);
	return text(sitemapXml);
};

When you now navigate to /sitemap.xml/ and look you should see the following output:

<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
</urlset>

Great, now we have a sitemap that is generated on demand. But it is still empty. Let’s add some entries.

Adding entries to the sitemap

Now that we have a basic sitemap, we can start adding entries to it. For this example, we’ll add the entries for all blog posts.

First, we need to import all the posts from a mysterious method getPosts(), which return all posts in the page. I can’t show you how you can implement it on your website, since it depends on how you store and create your posts.

/**
 * This function creates a sitemap entry for a post
 * @param post The post to create the entry for
 */
const postToEntry = (post: Post) => {
	return {
		loc: `https://www.example.com/posts/${post.slug}`,
		lastmod: post.last_modified,
		changefreq: 'monthly',
		priority: 0.8
	};
};

export const GET = async () => {
	// ...

	// First we retrieve all posts from our posts method
	const posts: Post[] = getPosts();

	const sitemap = {
		'?xml': {
			'@_version': '1.0',
			'@_encoding': 'UTF-8'
		},
		urlset: {
			'@_xmlns': 'http://www.sitemaps.org/schemas/sitemap/0.9',

			// We're adding the urls here
			url: [
				// Maybe add the homepage as a static entry
				{
					loc: 'https://www.example.com/',
                    changefreq: "daily",
                    priority: 1
				}

				// Run all posts through the postToEntry function
                ...posts.map(postToEntry)
			]
		}
	};

	// ...
};

Now, when you navigate to /sitemap.xml/ you should see all your posts listed in the sitemap.

<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
  <url>
    <loc>https://www.example.com/</loc>
    <changefreq>daily</changefreq>
    <priority>1</priority>
  </url>
  <url>
    <loc>https://www.example.com/posts/2024-04-22-some-post</loc>
    <lastmod>2024-04-22</lastmod>
    <changefreq>weekly</changefreq>
    <priority>0.8</priority>
  </url>
  <url>
    <loc>https://www.example.com/posts/2024-04-18-first-post</loc>
    <lastmod>2024-04-18</lastmod>
    <changefreq>weekly</changefreq>
    <priority>0.8</priority>
  </url>
</urlset>

What to do next?

You can now add more entries to the sitemap, e.g. for your tags, categories, or other pages like the about page or contact page.

The only thing you need to do is fetch the data and add it to the as entries to urls.