Home /

docs

Sitemaps

Generate both a standard sitemap.xml and a semantic sitemap.md from the same docs source. The XML file gives search crawlers a canonical URL list with freshness dates. The Markdown file gives agents, contributors, and automation a readable map of the docs tree with titles, descriptions, markdown URLs, last-updated dates, and related pages.

Quick Start

Enable sitemaps in docs.config.ts:

docs.config.ts
import { defineDocs } from "@farming-labs/docs";

export default defineDocs({
  entry: "docs",
  sitemap: {
    enabled: true,
    baseUrl: "https://docs.example.com",
  },
});

That exposes these routes through the same docs server wrapper and public forwarding layer:

Next.js withDocs() adds the public rewrites automatically. TanStack Start, SvelteKit, Astro, and Nuxt use the same generated public forwarder that already handles .md, llms.txt, .well-known, and MCP routes.

Static Export

For static hosting, run the generator before your framework build:

terminal
pnpm exec docs sitemap generate
pnpm exec docs robots generate

The command writes:

SvelteKit uses static/ instead of public/.

docs sitemap generate is enough for static output. The --public flag is only an explicit spelling for the default behavior. Use --manifest-only when a server-rendered app only needs the generated manifest for accurate lastmod values and should keep the public route handled by the runtime. Use docs robots generate --append if the project already has its own robots.txt and should keep site-specific rules around the generated agent policy block.

terminal
pnpm exec docs sitemap generate --manifest-only

Use a custom config path for adapters whose config lives outside the project root:

terminal
pnpm exec docs sitemap generate --config src/lib/docs.config.ts

Common build scripts:

package.json
{
  "scripts": {
    "build": "docs sitemap generate --manifest-only && next build"
  }
}

For output: "export", drop --manifest-only so public/sitemap.xml and public/sitemap.md are written before next build.

package.json
{
  "scripts": {
    "build": "docs sitemap generate --config src/lib/docs.config.ts && astro build"
  }
}
package.json
{
  "scripts": {
    "build": "docs sitemap generate --config src/lib/docs.config.ts && vite build"
  }
}
package.json
{
  "scripts": {
    "build": "docs sitemap generate && vite build"
  }
}
package.json
{
  "scripts": {
    "build": "docs sitemap generate && nuxt build"
  }
}

When you dogfood unpublished workspace packages, you may need to call the built CLI file directly after building the package. Published apps should use docs in package scripts or pnpm exec docs from the terminal.

What lastmod Means

lastmod is the last known content update date for a page. It appears as <lastmod> in sitemap.xml and as Last updated: in sitemap.md.

The CLI resolves dates in this order:

  1. the last git commit date for the page source file
  2. the filesystem modified date for the page source file
  3. no date, if neither source is available

Git dates are preferred because they are stable across machines and CI runs. Filesystem dates are a fallback for new files, generated content, or projects that are not in a git checkout.

Freshness is useful because crawlers and agents can:

If you do not want freshness dates, disable them per output:

docs.config.ts
export default defineDocs({
  sitemap: {
    xml: {
      includeLastmod: false,
    },
    markdown: {
      includeLastmod: false,
    },
  },
});

Route Prefix

Use routePrefix when you want the sitemap routes under a custom prefix:

docs.config.ts
export default defineDocs({
  sitemap: {
    enabled: true,
    routePrefix: "/docs-map",
    baseUrl: "https://docs.example.com",
  },
});

This generates:

There is no separate well-known route setting. The prefix applies to all sitemap routes together so adapters only need one forwarding rule.

Configuration Reference

docs.config.ts
export default defineDocs({
  sitemap: {
    enabled: true,
    routePrefix: "",
    baseUrl: "https://docs.example.com",
    manifestPath: ".farming-labs/sitemap-manifest.json",
    xml: {
      enabled: true,
      includeLastmod: true,
    },
    markdown: {
      enabled: true,
      includeDescriptions: true,
      includeLastmod: true,
      linkTarget: "both",
    },
  },
});
OptionTypeDefaultDescription
sitemapboolean | DocsSitemapConfigfalseEnable or configure sitemap routes.
sitemap.enabledbooleantrue when sitemap is an objectDisable without removing the object.
sitemap.routePrefixstring""Prefix all public sitemap routes.
sitemap.baseUrlstringrequest origin at runtime, llmsTxt.baseUrl in the CLIAbsolute base URL for XML <loc> values.
sitemap.manifestPathstring.farming-labs/sitemap-manifest.jsonInternal generated manifest path.
sitemap.xmlboolean | SitemapXmlConfigenabledEnable or configure sitemap.xml.
sitemap.xml.includeLastmodbooleantrueInclude <lastmod> when a page date is known.
sitemap.markdownboolean | SitemapMarkdownConfigenabledEnable or configure sitemap.md.
sitemap.markdown.includeDescriptionsbooleantrueInclude page descriptions in the Markdown sitemap.
sitemap.markdown.includeLastmodbooleantrueInclude Last updated: lines when a page date is known.
sitemap.markdown.linkTarget"html" | "markdown" | "both""both"Choose whether list items link to HTML pages, Markdown pages, or both.

Output Examples

sitemap.xml

sitemap.xml
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
  <url>
    <loc>https://docs.example.com/docs</loc>
    <lastmod>2026-05-08</lastmod>
  </url>
  <url>
    <loc>https://docs.example.com/docs/installation</loc>
    <lastmod>2026-05-08</lastmod>
  </url>
</urlset>

sitemap.md

sitemap.md
# Example Docs Sitemap

Base URL: https://docs.example.com
Docs entry: /docs
Generated: 2026-05-08

## Overview

- [Introduction](/docs)
  Markdown: /docs.md
  Description: Start here
  Last updated: 2026-05-08

## Installation

- [Installation](/docs/installation)
  Markdown: /docs/installation.md
  Description: Install the docs framework
  Last updated: 2026-05-08
  Related: /docs/configuration

Manifest

.farming-labs/sitemap-manifest.json
{
  "version": 1,
  "generatedAt": "2026-05-08T00:00:00.000Z",
  "baseUrl": "https://docs.example.com",
  "entry": "docs",
  "siteTitle": "Example Docs",
  "pages": [
    {
      "url": "/docs/installation",
      "absoluteUrl": "https://docs.example.com/docs/installation",
      "markdownUrl": "/docs/installation.md",
      "title": "Installation",
      "description": "Install the docs framework",
      "sourcePath": "docs/installation/page.mdx",
      "lastmod": "2026-05-08",
      "lastmodSource": "git",
      "related": ["/docs/configuration"]
    }
  ]
}

Verification

After enabling the feature, check the public routes:

terminal
curl "http://127.0.0.1:3000/sitemap.xml"
curl "http://127.0.0.1:3000/sitemap.md"
curl "http://127.0.0.1:3000/.well-known/sitemap.md"
curl "http://127.0.0.1:3000/robots.txt"

In CI, use --check to fail when generated static output is stale:

terminal
pnpm exec docs sitemap generate --check

Use the agent doctor when you want the sitemap to count toward the broader machine-facing surface:

terminal
pnpm exec docs doctor --agent
pnpm exec docs doctor --agent --url https://docs.example.com

The hosted doctor reads sitemap routes from /.well-known/agent.json, so custom routePrefix values are checked through the configured routes instead of assuming root-level defaults.