Dynamic Sitemap with Next.js and Supabase
Start by creating pages/sitemap.xml.tsx
.
Querying Supabase for Pages
We manage pages and their metadata in a post
table.
We'll write a query that selects pages
- configured to be indexed
- that are published
- ordered by most recently modified
import {supabase} from '@lib/supabase';
import {DBPost} from '@lib/types';
async function getPages(): Promise<DBPost[]> {
const {data: pages, error} = await supabase
.from<DBPost>('post')
.select()
.eq('noindex', false)
.not('date_published', 'is', null)
.order('date_modified', {ascending: false});
if (error) {
console.error(`failed to fetch post data: ${error.message}`);
return [];
}
if (!pages) {
console.warn(`did not get any pages back`);
return [];
}
return pages;
}
Adapt this implementation as needed, whether that's using Supabase, the filesystem, or any other CMS.
With url
and date_modified
for each page handy, we can build a sitemap.
Generating the Sitemap in JavaScript
Our getSitemap
function iterates over the pages we
fetched in the previous step and generates a <url>
component
for each.
I'm using date-fns
to format date_modified
(stored as ISO)
in a way that's consistent with Google's documentation on
how to build a
sitemap.
import {DBPost} from '@lib/types';
import {format} from 'date-fns';
function getSitemap(pages: DBPost[]) {
return `<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
${pages
.map(
(page) => `<url>
<loc>${page.url}</loc>
<lastmod>${format(
new Date(page.date_modified),
'yyyy-MM-dd',
)}</lastmod>
</url>`,
)
.join('')}
</urlset>
`;
}
We have the pieces in place to write getServerSideProps
.
Generating the Sitemap in getServerSideProps
We'll set Content-Type
, write our sitemap,
and return an empty set of props.
import {GetServerSideProps} from 'next';
export const getServerSideProps: GetServerSideProps<{}> = async ({res}) => {
res.setHeader('Content-Type', 'text/xml');
res.write(getSitemap(await getPages()));
res.end();
return {
props: {},
};
};
Finally, we'll appease the framework by exporting a default function from our route.
export default function Sitemap() {
return null;
}
Our sitemap is ready!
When we visit the site at /sitemap.xml
,
we should see our sitemap, ready to be crawled.