Content
Emissions
Machine-facing serializations of the site. sitemap.xml for crawlers, llms.txt for models, RSS for readers, raw markdown per page. Each is a one-line GET handler off the same outline.
Docs have a second audience in 2026: crawlers, language models, feed readers. The site layer emits the formats that they expect, directly off the outline: no parallel content, no separate build step. Each is a GET handler that you mount in a +server.ts.
When to use
Add the emissions that your audience asks for. A public docs site wants sitemap.xml and llms.txt. A blog wants RSS. An “open in your editor / copy as markdown” feature wants the raw source. They cost one line each.
The catalog
// src/routes/sitemap.xml/+server.ts — every leaf of the outline
import { docs } from '$lib/site.server';
export const prerender = true;
export const GET = docs.emit.sitemap({ base: '/docs', origin: 'https://example.dev' });// src/routes/llms.txt/+server.ts — the llmstxt.org model-readable map
import { docs } from '$lib/site.server';
export const prerender = true;
export const GET = docs.emit.llms({ title: 'Example', description: 'Docs for Example.' });// src/routes/docs/[...slug].md/+server.ts — the pre-compile source, per page
import { docs } from '$lib/site.server';
export const { GET, entries } = docs.emit.raw(); // strips frontmatter by default// src/routes/blog/rss.xml/+server.ts — the blog genre's feed
import { docs, blog } from '$lib/site.server';
export const prerender = true;
export const GET = docs.emit.rss({
title: 'Example blog',
base: '/blog',
items: async () =>
(await blog.refs()).map((r) => ({
href: `/blog/${r.id}`,
title: r.data.title,
description: r.data.description,
date: r.data.date
}))
});// src/routes/docs/search.json/+server.ts — the index the client worker queries
import { docs } from '$lib/site.server';
export const prerender = true;
export const GET = docs.emit.search();Sitemap and llms.txt
Both are views of the nav tree: each leaf, in reading order. That is the property worth having. A new page joins the sitemap and the model index the moment that the outline sees it. Neither can drift from the site, because neither is a second copy of anything.
origin matters when prerendered. At build time, there is no request to read an origin from. Thus absolute URLs come from the option, or from site({ data: { origin } }), which each emission defaults to:
export const docs = site({
outline: docs,
data: { title: 'Example', description: 'Docs for Example.', origin: 'https://example.dev' }
});
// now emissions need nothing:
export const GET = docs.emit.sitemap({ base: '/docs' });Raw markdown
The pre-compile source of each page, for “copy as markdown”, or for an LLM that wants the real thing. Mount it at a catch-all .md route. It prerenders one file per entry that carries source, and entries comes with it:
export const { GET, entries } = docs.emit.raw();
// docs.emit.raw({ frontmatter: 'keep' }) // …keep the frontmatter block insteadNow /docs/routing.md serves the source of /docs/routing. This site does exactly that: append .md to a docs URL, this page included.
Why the source and not the rendered HTML?
The rendered page already exists at the .html address. A second HTML serialization adds nothing. The .md emission is the authored artifact: stable under restyles, cheap to diff, and the representation that models quote most faithfully. The one applied transformation is frontmatter stripping (by default), because YAML metadata is site plumbing, not prose.
RSS
A feed over dated items: the emission of the blog genre. items maps the refs of a collection (RssItem is { href, title, description?, date }). Hrefs are root-relative, and made absolute against the origin at emit time. The emitter sorts again by date, descending. Thus the feed order is always correct.
Do / don’t
- Do emit off the outline, not off a second copy of the content. The sitemap and llms.txt are always in sync, because they are views of the same tree.
- Do put
originindocs.dataone time. Do not repeat it per emission. - Don’t hand-write these files. A new page joins the sitemap, the llms index, and the raw route the moment that it exists. That is the point.