alepha@docs:~/framework/docs/guides/frontend$
cat 7-sitemap.md | pretty
3 min read
Last commit:

#Sitemap

$sitemap serves a sitemap.xml built from your $page primitives, and the build prerenders it to a static file. You add one line to a router, and it follows the pages from then on.

typescript
1import { $sitemap } from "alepha/react/sitemap";2 3class AppRouter {4  sitemap = $sitemap();5}

The route is GET /sitemap.xml. The build writes it to dist/public/sitemap.xml, which is how a static site gets one. That file is also what a built server answers with, because static files mount over the route, so hostname and <lastmod> are the build's. alepha dev answers live. With static: false the build writes no file, so a built server answers live too, and a static site has no sitemap.

#Options

Option Default What it does
hostname PUBLIC_URL, else "" The base of every URL. Empty, the URLs are relative.
urls [] Extra URLs to list after the pages.
path /sitemap.xml Where it is served.
static true Whether the build prerenders it to a file.

⚠️ A sitemap is meant to hold absolute URLs, so set hostname or PUBLIC_URL. A prerendered file is written at build time, so PUBLIC_URL must be set then.

#Which pages it lists

It reads the router's compiled routes, so a nested page appears at the path a visitor requests, with its layouts in front of it.

Page Listed
A page with no parameter Yes.
A page with a parameter, its own or a layout's Only through static.entries, see below.
A layout (a page with children) No.
A path with a wildcard No.
/404 No.

There is no option to leave out one page, and a $secure page with no parameter is listed like any other.

#Pages with parameters

A page with parameters is listed once per entry of its static.entries:

typescript
 1import { z } from "alepha"; 2import { $page } from "alepha/react/router"; 3import { $sitemap } from "alepha/react/sitemap"; 4  5class BlogRouter { 6  post = $page({ 7    path: "/blog/:slug", 8    schema: { params: z.object({ slug: z.text() }) }, 9    static: {10      entries: [11        { params: { slug: "hello-world" } },12        { params: { slug: "second-post" } },13      ],14    },15    component: () => null,16  });17 18  sitemap = $sitemap({ hostname: "https://myapp.com" });19}

Values are percent-encoded. An entry that leaves a parameter unfilled, such as a parent's :orgId, is skipped, since a URL holding :orgId is not a URL.

#URLs that are not pages

urls lists what no $page answers, a feed or an llms.txt. A path is joined to the hostname, and an absolute URL is listed as it is:

typescript
1import { $sitemap } from "alepha/react/sitemap";2 3class AppRouter {4  sitemap = $sitemap({5    hostname: "https://myapp.com",6    urls: ["/llms.txt", "/feed.xml", "https://docs.myapp.com/"],7  });8}

#What it writes

A standard urlset, one <url> per entry, each with a <loc> and a <lastmod>. <lastmod> is the date the sitemap was generated, the same for every URL: it says nothing about when a page changed.

$sitemap does nothing in the browser, so it can sit on an isomorphic router class.