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.
Search
Onedocs uses the built-in Orama search from Fumadocs. Create an API route:
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:
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:
import { generateRobots } from "onedocs/seo";
const baseUrl = "https://yourdomain.com";
export default function robots() {
return generateRobots({ baseUrl });
}
This creates:
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:
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:
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:
import { defineDocs } from "fumadocs-mdx/config";
export const docs = defineDocs({
dir: "content/docs",
docs: {
postprocess: {
includeProcessedMarkdown: true,
},
},
});
Configuration options
Sitemap options
generateSitemap({
baseUrl: "https://yourdomain.com",
pages: ["index", "getting-started", "configuration"],
docsPath: "/docs", // Default: "/docs"
});
Robots options
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 |