SvelteKit came out in public beta a little over a month ago and I've finally gotten around to trying it out. I'll write up my thoughts elsewhere but I've moved r-bt.com over to SvelteKit and replaced my Notion CMS with markdown. The reason being I want to be able to use custom components. Anyway, one problem I had was creating a sitemap.xml for my static build. SvelteKit dosn't support creating sitemaps automatically although it might in the future.
Instead I made a post build step. Some notes about this:
- I'm using Node v14 if you use an earlier version you might need to change
importtorequire - I use
@sveltejs/adapter-staticto build a static site which is stored in/build
The Script
1. Install the dependencies
npm install -D fast-glob xmlbuilder2
2. Create a new file generate-sitemap.xml in the root of your project (e.g. beside svelte.config.cjs, etc) and add the following:
import fs from 'fs';
import fg from 'fast-glob';
import { create } from 'xmlbuilder2';
import pkg from './package.json';
const getUrl = (url) => {
const trimmed = url.slice(6).replace('index.html', '');
return `${pkg.url}/${trimmed}`;
};
async function createSitemap() {
const sitemap = create({ version: '1.0' }).ele('urlset', {
xmlns: 'http://www.sitemaps.org/schemas/sitemap/0.9'
});
const pages = await fg(['build/**/*.html']);
pages.forEach((page) => {
const url = sitemap.ele('url');
url.ele('loc').txt(getUrl(page));
url.ele('changefreq').txt('weekly');
});
const xml = sitemap.end({ prettyPrint: true });
fs.writeFileSync('build/sitemap.xml', xml);
}
createSitemap();
3. Update your package.json
{
url: "https://your-url.com",
scripts: {
...,
"postbuild": "node --experimental-json-modules ./generate-sitemap.js",
}
}
The Explaination
To make the sitemap we're going to build the site, glob all the .html files, and write the xml back to the /build directory.
Before starting install the dependecies
npm install -D fast-glob xmlbuilder2
Now create a new file generate-sitemap.xml
First, let's get the files we need:
import fg from 'fast-glob';
async function createSitemap() {
const pages = await fg(['build/**/*.html']);
console.log({ pages });
}
If you run this you should get an array with the paths of all your pages
{
pages: [
'build/index.html',
'build/blog/index.html',
'build/about/index.html',
'build/learning/index.html',
...
];
}
Next we'll use xmlbuilder to create the xml objects
import { create } from 'xmlbuilder2';
const sitemap = create({ version: '1.0' }).ele('urlset', {
xmlns: 'http://www.sitemaps.org/schemas/sitemap/0.9'
});
and we just loop through the pages adding each as a url object with a loc and changefrequency to the sitemap
pages.forEach((page) => {
const url = sitemap.ele('url');
url.ele('loc').txt(page);
url.ele('changefreq').txt('weekly');
});
Finally we turn the sitemap into a string and write it to a file using fs.writeFileSync
import fs from 'fs';
import fg from 'fast-glob';
import { create } from 'xmlbuilder2';
async function createSitemap() {
const sitemap = create({ version: '1.0' }).ele('urlset', {
xmlns: 'http://www.sitemaps.org/schemas/sitemap/0.9'
});
const pages = await fg(['build/**/*.html']);
console.log({ pages });
pages.forEach((page) => {
const url = sitemap.ele('url');
url.ele('loc').txt(page);
url.ele('changefreq').txt('weekly');
});
const xml = sitemap.end({ prettyPrint: true });
fs.writeFileSync('build/sitemap.xml', xml);
}
createSitemap();
Except we have a problem. If you run this code:
node generate-sitemap.js
and go to build/sitemap.xml you'll see that the locs are something that looks like:
build/learning/why-is-it-so-hard-to-find-a-domain/index.html
while we want it to be:
https://r-bt.com/learning/why-is-it-so-hard-to-find-a-domain/
To fix this go to your package.json and add
{
"url": "https://your-url.com"
}
Then in generate-sitemap.js we'll import package.json and append the url to the pages paths. We'll also remove the first 5 characters build/ and index.html
import pkg from './package.json';
const getUrl = (url) => {
const trimmed = url.slice(6).replace('index.html', '');
return `${pkg.url}/${trimmed}`;
};
Node.js dosn't yet importing .json files so need to run this script with the
--experimental-json-modules flag
node --experimental-json-modules ./generate-sitemap.js
and you're sitemap should be generated and valid π
To get it to run whenever you build the site go back to package.json and in scripts add
{
scripts:{
...,
"postbuild": "node --experimental-json-modules ./generate-sitemap.js",
}
}
Top comments (3)
Time travel from 4 years later, I think the cleanest way to build sitemap in SvelteKit now is to use
super-sitemap. Here is the snippet of usingsuper-sitemapto build sitemap in SvelteKit. Hopefully this is helpful.Can you elaborate on where the file should be? in root? in src?
It can be anywhere but best would be in the root of your project
e.g.
If you want to put it somewhere else you'll need to reflect that in the
prebuildcommand. I've updated the post to make this clearer, thanks for the feedback!