Kyle Nazario

How to create a sitemap for your SvelteKit blog

How to create a sitemap for your SvelteKit blog

DALL-E 2 - "a map with the google logo in the upper left. Cute, cartoony, pastel colors"

You can boost your SvelteKit blog’s SEO by creating a sitemap. To quote Google’s documentation:

A sitemap is a file where you provide information about the pages, videos, and other files on your site, and the relationships between them. Search engines like Google read this file to crawl your site more efficiently.

Building a sitemap is similar to creating an RSS feed.

Steps

First, install xmlbuilder2 using NPM.

Second, create a function called getAllPosts() that returns an array of file paths to your blog posts. See this guide or my implementation.

Third, add a folder in src/routes called sitemap.xml. Create a file +server.ts inside this folder. At the top of the file, write:

// src/routes/sitemap.xml/+server.ts

export const prerender = true;

export async function GET() {
  const headers = {
    'Cache-Control': 'max-age=0, s-maxage=3600',
    'Content-Type': 'application/xml'
  };
  return new Response(await getSitemapXml(), { headers });
}

When SvelteKit builds the site, it will create sitemap.xml at yourdomain.com/sitemap.xml. We’ll give this URL to Google later.

Build the XML

We’ll use xmlbuilder2 to create the sitemap without messy string interpolation.

// src/routes/sitemap.xml/+server.ts
// ...

async function getSitemapXml(): Promise<string> {
  const allPosts = await getAllPosts();
  const root = create({ version: '1.0', encoding: 'utf-8' })
  .ele('urlset', {
    xmlns: 'http://www.sitemaps.org/schemas/sitemap/0.9',
    'xmlns:xhtml': 'http://www.w3.org/1999/xhtml',
    'xmlns:mobile': 'http://www.google.com/schemas/sitemap-mobile/1.0',
    'xmlns:image': 'http://www.google.com/schemas/sitemap-image/1.1',
    'xmlns:news': 'http://www.google.com/schemas/sitemap-news/0.9',
    'xmlns:video': 'http://www.google.com/schemas/sitemap-video/1.1',
  })

// ...

This will produce an XML shell…

<?xml version="1.0" encoding="utf-8"?>
<urlset
    xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
    xmlns:xhtml="http://www.w3.org/1999/xhtml"
    xmlns:mobile="http://www.google.com/schemas/sitemap-mobile/1.0"
    xmlns:image="http://www.google.com/schemas/sitemap-image/1.1"
    xmlns:news="http://www.google.com/schemas/sitemap-news/0.9"
    xmlns:video="http://www.google.com/schemas/sitemap-video/1.1" />

…which we can fill in with links to our blog posts.

// src/routes/sitemap.xml/+server.ts
// ...

  for await (const post of allPosts) {
    const postUrl = `${BLOG_URL}/blog/${post.postPath}`;
    const pubDate = post.metadata.date;

    root.ele('url')
      .ele('loc').txt(postUrl).up()
      .ele('lastmod').txt(pubDate).up()
    .up();
  }

// ...

BLOG_URL comes from a file of sitewide constants:

// src/lib/blog-metadata.ts
// ...

export let BLOG_URL: string;
if (dev) {
  BLOG_URL = 'http://localhost:5173';
} else {
  BLOG_URL = 'https://kylenazario.com';
}

// ...

loc gives Google the post URL, and lastMod the last modified date. post.metadata.date comes from the frontmatter in my posts. Each post file is prefixed by:

---
title: Blog post title here
date: 2024-01-05
---

The final version of +server.ts looks like:

// src/routes/sitemap.xml/+server.ts

import { BLOG_URL } from '$lib/blog-metadata';
import { getAllPosts } from '$lib/post-handlers';
import { create } from 'xmlbuilder2';

export const prerender = true;

export async function GET() {
  const headers = {
    'Cache-Control': 'max-age=0, s-maxage=3600',
    'Content-Type': 'application/xml'
  };
  return new Response(await getSitemapXml(), { headers });
}

// prettier-ignore
async function getSitemapXml(): Promise<string> {
  const allPosts = await getAllPosts();
  const root = create({ version: '1.0', encoding: 'utf-8' })
  .ele('urlset', {
    xmlns: 'http://www.sitemaps.org/schemas/sitemap/0.9',
    'xmlns:xhtml': 'http://www.w3.org/1999/xhtml',
    'xmlns:mobile': 'http://www.google.com/schemas/sitemap-mobile/1.0',
    'xmlns:image': 'http://www.google.com/schemas/sitemap-image/1.1',
    'xmlns:news': 'http://www.google.com/schemas/sitemap-news/0.9',
    'xmlns:video': 'http://www.google.com/schemas/sitemap-video/1.1',
  })

  for await (const post of allPosts) {
    const postUrl = `${BLOG_URL}/blog/${post.postPath}`;
    const pubDate = post.metadata.date;

    root.ele('url')
      .ele('loc').txt(postUrl).up()
      .ele('lastmod').txt(pubDate).up()
    .up();
  }

  return root.end()
}

Publish your site. Make sure you can see the XML file content at yourdomain.com/sitemap.xml.

Last, follow Google’s instructions to submit a sitemap in the Search Console. They will periodically crawl your site from now on to get an index of pages.