Skip to content

SEO & Search

Search comes from Fumadocs. Onedocs adds helpers for the sitemap, robots.txt and llms.txt files, all set up as Next.js App Router routes. For page metadata and Open Graph images, see onedocs/metadata and onedocs/og in the API Reference.

Onedocs uses the built-in Orama search from Fumadocs. Create an API route:

TypeScript
import { source } from "@/lib/source";
import { createFromSource } from "fumadocs-core/search/server";

export const { GET } = createFromSource(source);

The search dialog that RootProvider adds queries /api/search by default, so no other setup is needed.

Sitemap

Generate a sitemap at /sitemap.xml:

TypeScript
import { generateSitemap } from "onedocs/seo";
import meta from "../../content/docs/meta.json";

const baseUrl = "https://yourdomain.com";

export default function sitemap() {
  return generateSitemap({
    baseUrl,
    pages: meta.pages ?? [],
  });
}

generateSitemap lists the site root, then one URL per entry in pages under /docs. Entries starting with --- (sidebar separators) are skipped, and index maps to /docs itself. It reads the entries as given, so pages inside subfolders need their own entries, for example "guides/setup".

Robots.txt

Generate a robots.txt at /robots.txt:

TypeScript
import { generateRobots } from "onedocs/seo";

const baseUrl = "https://yourdomain.com";

export default function robots() {
  return generateRobots({ baseUrl });
}

This creates:

Code
User-Agent: *
Allow: /

Host: https://yourdomain.com
Sitemap: https://yourdomain.com/sitemap.xml

LLMs.txt

LLMs.txt helps AI assistants understand your documentation. Onedocs can generate both summary and full-content versions.

Summary version

Create /llms.txt with page titles and descriptions:

TypeScript
import { createLLMsSource, generateLLMsText } from "onedocs/llms";
import { source } from "@/lib/source";
import config from "../../../onedocs.config";

export async function GET() {
  const llmsSource = createLLMsSource(source);
  const text = await generateLLMsText(llmsSource, {
    title: config.title,
    description: config.description,
  });

  return new Response(text, {
    headers: { "Content-Type": "text/plain; charset=utf-8" },
  });
}

The file starts with the title and description, lists every page as - Title: URL with its description, and ends with a pointer to /llms-full.txt. Both functions take the wrapped source from createLLMsSource, not the Fumadocs source itself, and return a promise.

Full content version

Create /llms-full.txt with complete documentation:

TypeScript
import { createLLMsSource, generateLLMsFullText } from "onedocs/llms";
import { source } from "@/lib/source";

export async function GET() {
  const llmsSource = createLLMsSource(source);
  const text = await generateLLMsFullText(llmsSource);

  return new Response(text, {
    headers: { "Content-Type": "text/plain; charset=utf-8" },
  });
}

Each page is written as its title, URL, description and processed Markdown, with --- between pages.

The page text comes from Fumadocs MDX's processed Markdown, which is off by default. Without it, llms-full.txt contains only titles, URLs and descriptions (#3). Turn it on in source.config.ts:

TypeScript
import { defineDocs } from "fumadocs-mdx/config";

export const docs = defineDocs({
  dir: "content/docs",
  docs: {
    postprocess: {
      includeProcessedMarkdown: true,
    },
  },
});

Configuration options

Sitemap options

TypeScript
generateSitemap({
  baseUrl: "https://yourdomain.com",
  pages: ["index", "getting-started", "configuration"],
  docsPath: "/docs", // Default: "/docs"
});

Robots options

TypeScript
generateRobots({
  baseUrl: "https://yourdomain.com",
  sitemapPath: "/sitemap.xml", // Default: "/sitemap.xml"
});

What you get

After setting up these routes:

URL What it does
/api/search?query=... Search API endpoint
/sitemap.xml XML sitemap for search engines
/robots.txt Robots file with sitemap reference
/llms.txt Summary for AI assistants
/llms-full.txt Full docs for AI assistants