How to Generate a Sitemap for your SvelteKit Website

Sitemaps help search engines prioritize pages within your site, particularly when you have a large amount of content.

How to Generate a Sitemap for your SvelteKit Website

You don’t need a sitemap if your site is small. This is not me, google tells you that.

Excerpt from this page

You might not need a sitemap if:

- Your site is "small". By small, we mean about 500 pages or fewer on your site. (Only pages that you think need to be in search results count toward this total.)

- Your site is comprehensively linked internally. This means that Google can find all the important pages on your site by following links starting from the homepage.

- You don't have many media files (video, image) or news pages that you want to show in search results. Sitemaps can help Google find and understand video and image files, or news articles, on your site. If you don't need these results to appear in image, video, or news results, you might not need a sitemap.

If you have less than 500 pages, you need not worry about sitemaps.

Your site is small but you need a sitemap

If you do not have dynamic pages (like blogs), you can just create a sitemap.xml file by referring to [this site and placing it inside /static directory.

This should give you a sitemap at yourdomain.tld/sitemap.xml

Sitemap in Sveltekit

Sveltekit 1.0 has a new handler called +server.js which can return a custom response instead of a usual HTML page. In this case, we want an XML response.

You can also use fetch to get all the routes if you’re fetching data from external API. You can then loop through it to put all those URLs into the sitemap.

You can refer to the official documentation to implement it or checkout the snippets below.

Create a directory with name `sitemap.xml“

This is the first step. The file routing system is quite useful in these cases. I think Sveltekit did a good job adopting directory-based routing.

Create a +server.js

Create a file +server.js inside sitemap.xml directory. Add the following content to the file.

sitemap.xml/+server.js

export async function GET() {
  return new Response(
    `
    <?xml version="1.0" encoding="UTF-8" ?>
    <urlset
      xmlns="https://www.sitemaps.org/schemas/sitemap/0.9"
      xmlns:xhtml="https://www.w3.org/1999/xhtml"
      xmlns:mobile="https://www.google.com/schemas/sitemap-mobile/1.0"
      xmlns:news="https://www.google.com/schemas/sitemap-news/0.9"
      xmlns:image="https://www.google.com/schemas/sitemap-image/1.1"
      xmlns:video="https://www.google.com/schemas/sitemap-video/1.1"
    >

      <!-- this is where all the urls go -->
       <url>
        <loc>https://domain.tld</loc> <!-- homepage -->
        <changefreq>daily</changefreq>
        <priority>0.7</priority>
      </url>
      <!-- this is where all the urls go -->

    </urlset>`.trim(),
    {
      headers: {
        "Content-Type": "application/xml",
      },
    }
  );
}

This is it. This should give you a working sitemap at /sitemap.xml route. You can hardcode more URLs to it along with the homepage.

Sometimes hard coding isn’t going to work. We may have dynamically generated pages. Check the snippets below to generate a dynamic sitemap for your Sveltekit website.

Fetch dynamic URLs from external API

If you’re getting data from an external API then you should get the slug for all the pages, posts, or articles.

sitemap.xml/+server.js

const getAllPosts = async () => {
  const res = await fetch("https://domain.tld/api/posts"); // your API to get posts
  const posts = await res.json();
  return posts;
};

export async function GET() {
  const posts = await getAllPosts();

  return new Response(
    `
    <?xml version="1.0" encoding="UTF-8" ?>
    <urlset
      xmlns="https://www.sitemaps.org/schemas/sitemap/0.9"
      xmlns:xhtml="https://www.w3.org/1999/xhtml"
      xmlns:mobile="https://www.google.com/schemas/sitemap-mobile/1.0"
      xmlns:news="https://www.google.com/schemas/sitemap-news/0.9"
      xmlns:image="https://www.google.com/schemas/sitemap-image/1.1"
      xmlns:video="https://www.google.com/schemas/sitemap-video/1.1"
    >

      <!-- this is where all the urls go -->
       <url>
        <loc>https://domain.tld</loc> <!-- homepage -->
        <changefreq>daily</changefreq>
        <priority>0.7</priority>
      </url>

      ${posts
        .map(
          (post) =>
            `
      <url>
        <loc>${SITE_URL}/${post.slug}</loc>
        <changefreq>daily</changefreq>
        <priority>0.7</priority>
      </url>
      `
        )
        .join("")}

    <!-- this is where all the urls go -->

    </urlset>`.trim(),
    {
      headers: {
        "Content-Type": "application/xml",
      },
    }
  );
}

Import all URLs from static markdown files

If you’re generating sitemaps for your markdown posts, you may have to import all the posts using import.meta.glob.

sitemap.xml/+server.js

const getAllPosts = async () => {
  const imports = import.meta.glob("/posts/**/*.md"); // make sure you get files from the right place
  let body = [];

  for (const path in imports) {
    body.push(
      imports[path]().then(({ metadata }) => {
        return {
          ...metadata, // may not be required for sitemap
          path,
        };
      })
    );
  }
  const posts = await Promise.all(body);

  return posts;
};

export async function GET() {
  const posts = await getAllPosts();

  return new Response(
    `
    <?xml version="1.0" encoding="UTF-8" ?>
    <urlset
      xmlns="https://www.sitemaps.org/schemas/sitemap/0.9"
      xmlns:xhtml="https://www.w3.org/1999/xhtml"
      xmlns:mobile="https://www.google.com/schemas/sitemap-mobile/1.0"
      xmlns:news="https://www.google.com/schemas/sitemap-news/0.9"
      xmlns:image="https://www.google.com/schemas/sitemap-image/1.1"
      xmlns:video="https://www.google.com/schemas/sitemap-video/1.1"
    >

      <!-- this is where all the urls go -->
       <url>
        <loc>https://domain.tld</loc> <!-- homepage -->
        <changefreq>daily</changefreq>
        <priority>0.7</priority>
      </url>

      ${posts
        .map(
          (post) =>
            `
      <url>
        <loc>${SITE_URL}/${post.slug}</loc>
        <changefreq>daily</changefreq>
        <priority>0.7</priority>
      </url>
      `
        )
        .join("")}

    <!-- this is where all the urls go -->

    </urlset>`.trim(),
    {
      headers: {
        "Content-Type": "application/xml",
      },
    }
  );
}

Thanks for reading.

Leave a comment if you have a suggestion.

Find related articles

Let's discuss on Twitter