Usage
Here are the general steps you’ll need
- Create your Cartographer instance
- Add your pages
- Generate your toolbelt
- Create a sitemap index page
- Create your sitemap entry page
Create your Cartographer instance
import { Cartographer } from "@modules/cartographer"
const sitemap = new Cartographer({ domain: "https://your-domain.com"})Parameters
domain(required): The domain for your site, it will be prepended to all your slugs.urlsPerPage(optional): The maximum number of sitemap entries you want per page. The maxiumum recommended by Google is 50,000, the package default is 10,000.priorityFn(optional): A method for generating the entrypriorityproperty. Default to a method returning0.7.defaultChangeFrequency(optional): The defaultchangefreqproperty. Defaults toweekly.defaultLastMod(optional): The defaultlastmodproperty. Defaults tonew Date(). If astringis provided, it will be used as is.logger(optional): A object with aerrorkey used for logging errors. By default it’ll useconsole.error.
Add your pages
sitemap returns a instance of a class with 3 methods. To add pages you can use the static or dynamic methods. The class implements the builder pattern, meaning you can chain these calls together or easily add a page based on some conditional (think multi-sites where certain categories of pages are not available).
As the names suggests, static is for static “hardcoded” URLs, while dynamic allows you to fetch the data from somewhere.
import { Cartographer } from "@modules/cartographer"
const sitemap = new Cartographer({ domain: "https://your-domain.com"})
sitemap.static('common', ['/', '/events'])
sitemap.dynamic('events', { fetcher: async (page, count, urlsPerPage) => {}, counter: async () => {}})static
The first key is the name used for the sitemap page, in the case above a /api/v1/sitemap/common.xml route will become available.
The second key are the routes you want to add to the sitemap. This can either be a string or a SitemapEntry if you want some more control over some of the values, if you didn’t want the defaults to apply.
dynamic
The first key is the name used for the sitemap page, in the case above a /api/v1/sitemap/events/0.xml route will become available.
The second parameter is a object with 2 keys.
fetcher- takes in the current page, total count (from thecounter) and maximum number of pages that can be on the page. You can use thecountandurlsPerPageto figure out API offset values. The function should return a list ofSitemapEntryrecords.counter- this function should return the total number of records for this category. This will use theurlsPerPageto determine how many individual pages this category needs. Imagine aSELECT count(*) FROM events;type query.
Generate your toolbelt
Once you’re happy with your setup, you can create the toolbelt by running the finalise method on the Cartographer instance.
const sitemap = new Cartographer({...}).static().dynamic()
export const cartographer = sitemap.finalise()This create a CartographerToolbelt instance and is what you’ll use moving forward, so I recommend exporting it from your file.
Steps 4 & 5
There are 2 methods on this toolbelt;
$survey- generates aResponsefor the sitemap index$draw- generates aResponsefor the individual pages
Depending on the framework used, how you implement these may vary. Here are some general rules using Next.js App Directory as an example.
- Create a
sitemap.xml/route.tsfile. It should export aGETmethod and return the response directly. - Create a dynamic route at
/api/v1/sitemap/[key]/[[...page]]/route.ts. It should export aGETmethod and return the response directly.
Directoryapp
Directorysitemap.xml
- route.ts
Directoryapi/v1/sitemap/[key]/[[…page]]
- route.ts
import { cartographer } from "@/modules/domain/sitemap"
export function GET() { return cartographer.$survey()}import type { Types } from "@/modules/cartographer/types"import { cartographer } from "@/modules/domain/sitemap"
export function GET(_: unknown, data: Types.Segment) { return cartographer.$draw(_, data)}Since Next.js App Directory supports returning a Response object, we can simply return it directly. The only part you want to keep in mind is that the $draw command takes in a unknown first parameter. This is a placeholder for a Request value that might become useful in the future. The second requires a object that has params with this following shape.
export type Segment = { params: Promise<{ key: string page?: string[] }>}It mimics the new Next v15 promise based parameters. Regardless of your framework you should be able to replicate this structure, if your params don’t come in as a Promise you can always wrap it in a Promise.resolve.
Weird API urls?
The individual sitemap pages do have weird API urls that needs to be taken care of to make them a bit more “readable”. Your framework may be able to handle this by tweaking where you place the file. With Next, you can use the rewrites property in the next.config file.
const nextConfig = { rewrites: () => [ { source: "/sitemap/:path*", destination: "/api/v1/sitemap/:path*", } ],}