# Documentation > An AI-native documentation framework for Next.js, TanStack Start, SvelteKit, Astro, and Nuxt. ## Introduction URL: https://docs.farming-labs.dev/docs An AI-native documentation framework that just works @farming-labs/docs An AI-native documentation framework built on top of Fumadocs and other preset providers. One config file, zero boilerplate, with docs that are ready for humans, IDEs, and agents out of the box. Why @farming-labs/docs? - Zero boilerplate — No layout files, no [[...slug]] wrappers except for fully fledged functionality like AI, Search and metadata to work. Just write Markdown or MDX. - One config — Everything in docs.config.ts: theme, colors, typography, icons, components, metadata. - Token efficient — Your entire docs framework surface is a single config file (~15 lines). AI tools like Cursor, Copilot, and Claude spend less time reading framework plumbing and more time working with your actual content. Built-in llms.txt serves your docs in LLM-optimized format automatically. Learn more → - Multiple frameworks — First-class support for Next.js, TanStack Start, SvelteKit, Astro, and Nuxt that just works. - 9 themes — Default, Darksharp, Pixel Border, Colorful, Shiny, DarkBold, GreenTree, Hardline, and Concrete — or build your own with createTheme(). - Built-in search — Zero-config simple search by default, or upgrade to Typesense, Algolia, or a custom adapter. - CLI scaffolding — npx @farming-labs/docs init first asks whether you're on an existing project or starting fresh; then it detects your framework, lets you pick a theme (or create your own), can scaffold locale folders for i18n, and generates config, routes, and sample pages. Use --template with --name to bootstrap a new project without prompts. other docs frameworks? We are not trying to replace Fumadocs — we aim to be complementary. @farming-labs/docs builds on Fumadocs (and other providers) to offer a single config, multi-framework support, and optional themes and tooling, so you can use Fumadocs with less boilerplate and across Next.js, TanStack Start, SvelteKit, Astro, and Nuxt. If you're choosing between documentation tools, here’s when @farming-labs/docs is a good fit: - vs. Fumadocs alone — You get the same UI and MDX experience, but with a single docs.config and first-class support for TanStack Start, SvelteKit, Astro, and Nuxt — not just Next.js. One config model and CLI so you don’t wire layouts, themes, and search by hand. - vs. Docusaurus / VitePress — You keep full control inside your existing app (Next, TanStack Start, SvelteKit, Astro, Nuxt) instead of a separate docs site. No separate framework or build; your docs live in the same repo and stack, with one config file and optional built-in AI chat and search. - vs. Mintlify / ReadMe / hosted docs — You own the code and hosting. No vendor lock-in or per-seat pricing; you can deploy to Vercel, Cloudflare Pages, or anywhere. Built-in llms.txt and AI chat give you AI-friendly docs without a third-party docs platform. - vs. hand-rolled MDX — No need to build layouts, catch-all routes, search, or theming yourself. The CLI scaffolds everything; you spend time on content and optional customization (themes, colors, sidebar, page actions) instead of framework plumbing. In short: use @farming-labs/docs when you want an AI-native docs runtime, one config, multiple frameworks, and minimal boilerplate — without leaving your current stack or adopting a separate docs-only framework. Customize themes and share them You can create your own theme and distribute it to others — as an npm package, a shared preset, or a CSS file. Themes control layout, colors, typography, and components; once built, anyone can plug them in via config. See Themes for built-in presets and Creating themes for the full API. Built-in docs UI — enable and customize from config Modern docs features are built in; you turn them on in configuration instead of wiring them yourself. Each element can be customized — layout, behavior, and appearance — via the same config and theme options. See Customization for the full overview. - Ask AI — RAG-powered chat, multiple models and providers, floating or sidebar mode. Customize labels, suggested questions, model list, and UI mode. - Search — Built-in simple search works out of the box, and you can switch to Typesense, Algolia, or a custom adapter from configuration. - Page actions — Copy Markdown, open in ChatGPT/other tools. Customize which actions appear and their options. - Sidebar — Collapsible, flat or nested, banner, footer. Customize structure, style, and content (banner, footer, nav title). - Components — Built-in MDX components like Callout, Tabs, and HoverLink, plus your own custom React components. - llms.txt — LLM-friendly index of your docs; served when the docs API is enabled. Options described in the llms.txt docs. Theme-level customization (colors, typography, components) is covered in Configuration and Customization (including Colors and Typography). No custom components or routes required — enable and tweak what you need in docs.config. Packages | Package | Description | | ---------------------------- | ------------------------------------------------------------------------------------ | | @farming-labs/docs | Core types, defineDocs(), createTheme(), extendTheme() | | @farming-labs/theme | Next.js theme presets, layout components, CSS presets | | @farming-labs/next | Next.js adapter — withDocs(), MDX processing, search API | | @farming-labs/tanstack-start | TanStack Start adapter — docs page renderer, MDX processing, search API, server loader | | @farming-labs/svelte | SvelteKit adapter — server-side docs loader, markdown processing | | @farming-labs/svelte-theme | SvelteKit theme presets, DocsLayout, DocsContent components | | @farming-labs/astro | Astro adapter — server-side docs loader, markdown processing | | @farming-labs/astro-theme | Astro theme presets, DocsLayout, DocsContent components | | @farming-labs/nuxt | Nuxt 3 adapter — defineDocsHandler(), server-side docs loader, markdown processing | | @farming-labs/nuxt-theme | Nuxt theme presets, DocsLayout, DocsContent components | Quick Start The fastest way to get started is with the CLI: The CLI first asks existing project or fresh? — then (for existing) auto-detects your framework, lets you pick a theme or create your own, can scaffold locale folders and i18n config, and generates config, routes, CSS, and sample pages. Prompts with a default (e.g. entry path docs) accept Enter to skip typing. See the CLI reference for the full init flow, or set up manually with the Installation guide. Next Steps - Token Efficiency — Why this is the most AI-friendly docs framework - Configuration — Customize your theme, colors, typography, sidebar, and more - Themes — Browse available themes and learn how to create your own - Customization — Colors, typography, sidebar, AI chat, page actions - Ask AI — Add RAG-powered AI chat to your docs - API Reference — Complete reference for every config option - Contributing — How to report issues, suggest features, and submit pull requests --- ## CLI URL: https://docs.farming-labs.dev/docs/cli Scaffold a docs project with one command CLI The @farming-labs/docs CLI has two main jobs: - init — create a new docs app or add docs to an existing app - upgrade — bump the docs packages in an existing project - mcp — run the built-in docs MCP server over stdio for local clients and IDE agents - search sync — push docs content into an external search index like Typesense or Algolia If you only need the common commands, start here. The framework-specific output and generated file lists are further down this page. Quick Start From scratch Use init --template when you want a brand new project directory with docs already set up. This creates a new folder, clones the example template, installs dependencies, and leaves you with a ready-to-run docs app. Existing project Use plain init inside an app you already have. Run that from your app root. The CLI will: 1. detect your framework from package.json or ask you to choose 2. ask for a theme 3. ask where docs should live, usually docs 4. ask about aliases, global CSS, and optional i18n when supported 5. generate the docs files and install the correct @farming-labs/* packages Upgrade an existing docs project Use upgrade when docs are already installed and you just want newer package versions. By default, the CLI detects your framework and package manager from the current project. If detection is ambiguous, pass --framework. Run the built-in MCP server locally Use mcp when you want to expose the current docs project to a local MCP client over stdio. If your config lives outside the project root default: To test the HTTP MCP route in this repo instead of stdio, start the Next example: Then point your MCP client or inspector at: Sync an external search index Use search sync when you want to pre-index docs into a hosted search backend instead of waiting for the first request to trigger sync. The command reads .env and .env.local from the current project, scans the docs content based on entry / contentDir, and uploads search documents to the selected provider. Typesense example: Required env: Optional: Algolia example: Required env: Init init without --template When you run init without --template, the CLI asks whether you're working with an existing project or starting from scratch. | Choice | Best for | What happens | | ------ | -------- | ------------ | | Existing project | You already have a Next.js, TanStack Start, Nuxt, SvelteKit, or Astro app | The CLI adds docs to the current app | | Fresh project | You want a new docs starter | The CLI creates a new folder from the selected example template | From scratch If you select Fresh project, the CLI asks for: 1. Framework — Next.js, TanStack Start, Nuxt, SvelteKit, or Astro 2. Project name — default is my-docs 3. Package manager — used for install and the suggested dev command You can skip those prompts entirely with --template and --name. Replace next with tanstack-start, nuxt, sveltekit, or astro. Existing project If you select Existing project, the CLI stays in the current folder and wires docs into the app you already have. 1. Framework — Auto-detected from package.json, or you pick one if detection fails. 2. Theme — One of the built-in themes or Create your own theme (you’ll be asked for a theme name; it creates themes/.ts and themes/.css). 3. Path aliases — Whether to use @/, $lib/, or ~/ for imports (framework-dependent). 4. Entry path — Where docs live (e.g. docs). Press Enter to use the default. 5. Optional API reference — Whether to scaffold API reference support. If you enable it, the CLI pre-fills the detected API route root (usually api) and lets you edit it before writing apiReference.routeRoot. 6. Optional i18n — Whether to scaffold internationalized docs, which locales to generate, any extra locale codes to add, and which locale should be the default. 7. Global CSS — Which file to use (auto-detected if there’s only one, otherwise you pick or type the path; Enter uses the default). 8. The CLI generates config, layout, and sample pages, then detects the package manager from a lockfile when possible. If no lockfile is found, it asks which package manager you want to use before installing dependencies, and can then start the dev server. Typical flow: Prompts that show a placeholder (e.g. entry path docs, project name my-docs, theme name my-theme) use that value as the default — you can press Enter to accept it instead of typing. Existing-project flags Use these to skip the matching prompts during init: | Flag | Description | | ---- | ----------- | | --theme | Skip the theme prompt | | --entry | Skip the docs entry prompt | | --api-reference | Enable API reference scaffold during init | | --no-api-reference | Explicitly skip API reference scaffold | | --api-route-root | Set the detected API route root written to apiReference.routeRoot | Example: --api-route-root is optional. If you omit it, the CLI tries to detect a sensible default from the framework’s route conventions and shows that value as the default in the prompt. When API reference is enabled through init: - Next.js writes the apiReference block and app/api-reference/[[...slug]]/route.ts (or src/app/...). - TanStack Start, SvelteKit, Astro, and Nuxt write the apiReference block and also scaffold the minimal /{path} handler files so the reference works immediately. Optional i18n scaffold If you enable i18n during init, the CLI will: 1. Let you multi-select common locales such as en, fr, es, de, pt, ja, ko, zh, ar, hi, and ru 2. Let you add custom locale codes such as pt-BR 3. Ask which locale should be the default The generated project gets: - An i18n block in docs.config.ts / src/lib/docs.config.ts - Locale content folders like docs/en, docs/fr, and docs/zh - Framework-specific starter files so routes like /docs?lang=en and /docs?lang=fr work out of the box Upgrade Upgrade all @farming-labs/ docs packages to the latest version. Run from your project root; the CLI auto-detects* your framework from package.json and upgrades the right packages for Next.js, TanStack Start, Nuxt, SvelteKit, and Astro. The CLI also detects your package manager from the lockfile in the current directory (pnpm-lock.yaml, yarn.lock, bun.lock, bun.lockb, or package-lock.json). If no lockfile is found, it asks which package manager you want to use instead of defaulting to npm. | Framework | Packages upgraded to @latest | | --------- | ------------------------------ | | Next.js | @farming-labs/docs, @farming-labs/theme, @farming-labs/next | | TanStack Start | @farming-labs/docs, @farming-labs/theme, @farming-labs/tanstack-start | | Nuxt | @farming-labs/docs, @farming-labs/nuxt, @farming-labs/nuxt-theme | | SvelteKit | @farming-labs/docs, @farming-labs/svelte, @farming-labs/svelte-theme | | Astro | @farming-labs/docs, @farming-labs/astro, @farming-labs/astro-theme | To specify the framework explicitly (e.g. in a monorepo or when auto-detect fails), use --framework: Valid values: next, tanstack-start, nuxt, sveltekit, astro. Use --latest (default) to install the latest stable release, or --beta to install beta versions: If you prefer the shorter command form, the CLI also accepts upgrade@beta and upgrade@latest. For example, pnpx @farming-labs/docs upgrade@beta behaves like pnpx @farming-labs/docs upgrade --beta. MCP Run the built-in docs MCP server over stdio from the current project. The command reads docs.config.ts[x] from the current project root by default and reuses the docs graph from your configured entry and contentDir. Use --config when the config lives somewhere else: The current built-in MCP surface includes: - list_pages - get_navigation - search_docs - read_page For the HTTP endpoint version, see MCP Server. Supported Frameworks | Framework | Detection | init | upgrade | | --------- | --------- | ------ | --------- | | Next.js | next in dependencies | Supported | Supported | | TanStack Start | @tanstack/react-start in dependencies | Supported | Supported | | SvelteKit | @sveltejs/kit in dependencies | Supported | Supported | | Astro | astro in dependencies | Supported | Supported | | Nuxt | nuxt in dependencies | Supported | Supported | Available Themes The init CLI offers several built-in themes plus an option to create your own: | Theme | Description | | -------------- | ---------------------------------------------------------- | | fumadocs | Clean, neutral docs theme (default) | | darksharp | All-black, sharp corners, no rounded edges | | pixel-border | Refined dark UI inspired by better-auth.com | | colorful | Fumadocs-style neutral theme with description support | | greentree | Emerald green accent, Inter font, Mintlify-inspired | | command-grid | Mono-first paper-grid preset inspired by the better-cmdk landing page | | concrete | Brutalist poster-style theme with offset shadows | | hardline | Hard-edge theme with square corners and bold borders | | darkbold | Pure monochrome, Geist typography, clean minimalism | | shiny | Glossy, modern look with subtle shimmer effects | | Create your own theme | Scaffolds themes/.ts and themes/.css; you’re prompted for the theme name (default my-theme). See Creating your own theme. | What it does (Existing project) When you choose Existing project, the CLI does the following (framework-specific files vary): 1. Detects Next.js via next in your package.json (or asks you to pick a framework) 2. Asks for a theme — built-in themes or Create your own theme (then prompts for theme name; press Enter for default my-theme) 3. Asks for the entry path — Default is docs (press Enter to accept) 4. Asks about path aliases — Use @/ alias or relative paths for imports 5. Optionally scaffolds i18n — Generates locale folders like app/docs/en and app/docs/fr, plus locale-aware wrapper pages 6. Generates files: - docs.config.ts — Full config with your selected theme - next.config.ts — Wraps your existing config with withDocs() - app/global.css — Imports Tailwind and theme CSS - app/layout.tsx — Root layout with RootProvider - app/docs/layout.tsx — Docs layout - app/docs/page.mdx — Sample documentation page when i18n is disabled - app/docs/installation/page.mdx — Sample installation guide when i18n is disabled - app/docs/quickstart/page.mdx — Sample quickstart guide when i18n is disabled 7. Installs dependencies — @farming-labs/docs, @farming-labs/theme, @farming-labs/next 8. Starts the dev server — Opens your docs at http://localhost:3000/docs 1. Detects TanStack Start via @tanstack/react-start in your package.json (or asks you to pick a framework) 2. Asks for a theme — built-in themes or Create your own theme (then prompts for theme name; press Enter for default my-theme) 3. Asks about path aliases — Use @/ alias or relative paths for imports 4. Asks for the entry path — Default is docs (press Enter to accept) 5. Skips the built-in i18n scaffold so the generated TanStack routes stay minimal and working 6. Generates files: - docs.config.ts — Full config with your selected theme - src/lib/docs.server.ts — TanStack docs server - src/lib/docs.functions.ts — Server function wrapper for page loading - src/routes/{entry}/index.tsx — Docs index route - src/routes/{entry}/$.tsx — Catch-all docs route - src/routes/api/docs.ts — Search and AI API route - src/styles/app.css — Imports Tailwind and theme CSS - docs/page.mdx — Sample documentation page - docs/installation/page.mdx — Sample installation guide - docs/quickstart/page.mdx — Sample quickstart guide 7. Updates existing app files when needed — Injects RootProvider into src/routes/root.tsx and adds docsMdx() / Tailwind plugins to vite.config.ts 8. Installs dependencies — @farming-labs/docs, @farming-labs/theme, @farming-labs/tanstack-start 9. Starts the dev server — Opens your docs at http://localhost:5173/docs > In this repo's examples and in other monorepos, keep those @farming-labs/ dependencies linked locally while testing unpublished changes. CI and Vercel on Node 22 should consume the built adapter entrypoints, not raw src/.ts files from node_modules. 1. Detects SvelteKit via @sveltejs/kit in your package.json (or asks you to pick a framework) 2. Asks for a theme — built-in themes or Create your own theme (then prompts for theme name; press Enter for default) 3. Asks for the entry path — Default is docs (press Enter to accept) 4. Asks about path aliases — Use $lib/ alias or relative paths for imports 5. Optionally scaffolds i18n — Generates locale folders like docs/en and docs/fr plus a docs root page for /docs?lang=... 6. Generates files (does not touch svelte.config.js): - src/lib/docs.config.ts — Full config with your selected theme - src/lib/docs.server.ts — Server-side docs loader with import.meta.glob for production builds - src/routes/docs/+layout.svelte — Docs layout component - src/routes/docs/+layout.server.js — Layout data loader - src/routes/docs/[...slug]/+page.svelte — Dynamic page component - src/routes/docs/+page.svelte — Generated when i18n is enabled - src/app.css — Theme CSS import - docs/page.md — Sample documentation page - docs/installation/page.md — Sample installation guide - docs/quickstart/page.md — Sample quickstart guide 7. Installs dependencies — @farming-labs/docs, @farming-labs/svelte, @farming-labs/svelte-theme 8. Starts the dev server — Opens your docs at http://localhost:5173/docs > Production note: The generated docs.server.ts uses import.meta.glob to bundle your markdown files at build time. This is required for serverless deployments (Vercel, Netlify, etc.) where the filesystem is not available at runtime. 1. Detects Astro via astro in your package.json (or asks you to pick a framework) 2. Asks for a theme — built-in themes or Create your own theme (then prompts for theme name; press Enter for default) 3. Asks for the entry path — Default is docs (press Enter to accept) 4. Optionally scaffolds i18n — Generates locale folders like docs/en and docs/fr 5. Generates files: - src/lib/docs.config.ts — Full config with your selected theme - src/lib/docs.server.ts — Server-side docs loader with import.meta.glob for production builds - src/pages/{entry}/index.astro — Docs index page - src/pages/{entry}/[...slug].astro — Dynamic page component - src/pages/api/{entry}.ts — API route for search and AI - astro.config.mjs — Astro config with SSR enabled - docs/page.md — Sample documentation page - docs/installation/page.md — Sample installation guide - docs/quickstart/page.md — Sample quickstart guide 6. Installs dependencies — @farming-labs/docs, @farming-labs/astro, @farming-labs/astro-theme 7. Starts the dev server — Opens your docs at http://localhost:4321/docs > Production note: The generated docs.server.ts uses import.meta.glob to bundle your markdown files at build time. This is required for serverless deployments (Vercel, Netlify, etc.) where the filesystem is not available at runtime. 1. Detects Nuxt via nuxt in your package.json (or asks you to pick a framework) 2. Asks for a theme — built-in themes or Create your own theme (then prompts for theme name; press Enter for default) 3. Asks for the entry path — Default is docs (press Enter to accept) 4. Optionally scaffolds i18n — Generates locale folders like docs/en and docs/fr 5. Generates files: - docs.config.ts — Full config with your selected theme - nuxt.config.ts — Nuxt config with theme CSS and server assets - server/api/docs.get.ts, server/api/docs.post.ts, server/api/docs/load.get.ts — docs data, search, AI, and page loading - pages/docs/[[...slug]].vue — Catch-all page component that handles the docs index and nested docs routes - docs/page.md — Sample documentation page - docs/installation/page.md — Sample installation guide - docs/quickstart/page.md — Sample quickstart guide 6. Installs dependencies — @farming-labs/docs, @farming-labs/nuxt, @farming-labs/nuxt-theme 7. Starts the dev server — Opens your docs at http://localhost:3000/docs Example Output Flags | Flag | Description | | ----------------------- | --------------------------------------------------------------------------- | | --template | Bootstrap a project: next, tanstack-start, nuxt, sveltekit, or astro. Use with --name. | | --name | Project folder name when using --template; prompt if omitted (e.g. my-docs) | | --theme | Skip theme prompt: fumadocs, darksharp, pixel-border, colorful, darkbold, shiny, greentree, concrete, command-grid, hardline | | --entry | Skip entry path prompt (e.g. --entry docs) | Starting from scratch? Use --template with --name . The CLI bootstraps a project with the given name, installs dependencies, and then you run cd && pnpm run dev to start. Manual Code Here is every file the CLI generates, ready to copy-paste if you prefer manual setup. For each framework, add your theme's CSS to the global stylesheet (or config) shown below. The import must match the theme you use in docs.config (e.g. default, greentree, darksharp). Without it, docs pages will lack the correct styling. If your config includes JSX or React nodes, rename docs.config.ts to docs.config.ts. This applies when you add things like a custom nav.title, React icons, or React-based components directly in the config file. docs.config.ts next.config.ts app/global.css — add your theme's CSS here so docs styling applies. app/layout.tsx app/docs/layout.tsx app/docs/page.mdx (sample page) > If you chose darksharp or pixel-border, the CSS import line will differ: > - Darksharp: @import "@farming-labs/theme/darksharp/css"; > - Pixel Border: @import "@farming-labs/theme/pixel-border/css"; docs.config.ts vite.config.ts src/styles/app.css — add your theme's CSS here so docs styling applies. src/routes/__root.tsx src/lib/docs.server.ts src/routes/docs/index.tsx > TanStack Start uses the same @farming-labs/theme theme package as Next.js. Keep the theme name in docs.config.ts and src/styles/app.css aligned. src/lib/docs.config.ts src/lib/docs.server.ts src/routes/docs/+layout.svelte src/routes/docs/+layout.server.js src/routes/docs/[...slug]/+page.svelte src/routes/api/docs/+server.js (search & AI) src/app.css — add your theme's CSS here so docs styling applies. docs/page.md (sample page) > If you chose darksharp or pixel-border, swap the theme factory and CSS: > - Darksharp: import { darksharp } from "@farming-labs/svelte-theme" + @import "@farming-labs/svelte-theme/darksharp/css"; > - Pixel Border: import { pixelBorder } from "@farming-labs/svelte-theme" + @import "@farming-labs/svelte-theme/pixel-border/css"; src/lib/docs.config.ts src/lib/docs.server.ts src/pages/docs/index.astro and src/pages/docs/[...slug].astro — import your theme's CSS in each (e.g. @farming-labs/astro-theme/css for default; use greentree/css etc. for other themes). src/pages/api/docs.ts (search & AI) astro.config.mjs docs/page.md (sample page) > If you chose darksharp or pixel-border, swap the theme factory and CSS: > - Darksharp: import { darksharp } from "@farming-labs/astro-theme" + @farming-labs/astro-theme/darksharp/css > - Pixel Border: import { pixelBorder } from "@farming-labs/astro-theme" + @farming-labs/astro-theme/pixel-border/css docs.config.ts nuxt.config.ts — add your theme's CSS in the css array so docs styling applies. server/api/docs.get.ts, server/api/docs.post.ts, and server/api/docs/load.get.ts (search, AI, and page loading) pages/docs/[...slug].vue docs/page.md (sample page) > If you chose darksharp or pixel-border, swap the theme factory and CSS: > - Darksharp: import { darksharp } from "@farming-labs/nuxt-theme" + @farming-labs/nuxt-theme/darksharp/css > - Pixel Border: import { pixelBorder } from "@farming-labs/nuxt-theme" + @farming-labs/nuxt-theme/pixel-border/css Project Structure After running init, your project will look like this: --- ## Configuration URL: https://docs.farming-labs.dev/docs/configuration Everything lives in docs.config.ts Configuration All configuration lives in a single docs.config.ts file. If your config includes JSX or React nodes, rename docs.config.ts to docs.config.tsx. This is common when setting things like nav.title, custom icons, or React-based components in the config. The config file lives at the project root as docs.config.tsx when it contains JSX: The config file lives at the project root as docs.config.tsx when it contains JSX: > TanStack Start, SvelteKit, Astro, and Nuxt require contentDir (path to your markdown files) and nav (sidebar title/URL) since routing is handled differently from Next.js. The config file lives at src/lib/docs.config.ts: > TanStack Start, SvelteKit, Astro, and Nuxt require contentDir (path to your markdown files) and nav (sidebar title/URL) since routing is handled differently from Next.js. The config file lives at src/lib/docs.config.ts: The config file lives at the project root as docs.config.ts: Config Options | Option | Type | Default | Description | | -------------- | ------------------------------ | --------------- | -------------------------------------------------- | | entry | string | "docs" | The docs URL path prefix (e.g. "docs" → /docs) | | contentDir | string | same as entry | Path to content files (TanStack Start, SvelteKit, Astro, Nuxt) | | staticExport | boolean | false | Set true for full static builds (see Static export) | | theme | DocsTheme | — | Theme preset from a theme factory | | nav | { title, url } | — | Sidebar title and base URL | | github | string \| GithubConfig | — | GitHub repo for "Edit on GitHub" links | | themeToggle | boolean \| ThemeToggleConfig | true | Light/dark mode toggle | | breadcrumb | boolean \| BreadcrumbConfig | true | Breadcrumb navigation | | sidebar | boolean \| SidebarConfig | true | Sidebar visibility and style | | icons | Record | — | Icon registry for frontmatter icon fields | | components | Record | — | Custom MDX components and built-in overrides like HoverLink | | onCopyClick | (data: CodeBlockCopyData) => void | — | Callback when the user clicks the copy button on a code block | | feedback | boolean \| FeedbackConfig | false | Human page feedback UI plus optional agent feedback endpoints | | pageActions | PageActionsConfig | — | Copy Markdown, Open in LLM buttons | | ai | AIConfig | — | RAG-powered AI chat | | search | boolean \| DocsSearchConfig | true | Built-in simple search, Typesense, Algolia, or a custom adapter | | changelog | boolean \| ChangelogConfig | false | Generated changelog feed and entry pages from dated MDX entries (Next.js) | | mcp | boolean \| DocsMcpConfig | enabled | Built-in MCP server over stdio and /api/docs/mcp | | apiReference | boolean \| ApiReferenceConfig | false | Generated API reference from framework route conventions or a hosted OpenAPI JSON | | i18n | DocsI18nConfig | — | Query-param locale support and locale switcher | | metadata | DocsMetadata | — | SEO metadata template | | og | OGConfig | — | Dynamic Open Graph images (see API Reference) | Machine-readable markdown routes No separate config flag is required for machine-readable page markdown. - The shared docs API supports GET /api/docs?format=markdown&path= - In Next.js, withDocs() also serves /docs/.md - In Next.js, sending Accept: text/markdown to /docs/ returns the same markdown response - Embedded ... blocks stay hidden in the normal UI but are included in the .md fallback and MCP read_page - When a page folder has agent.md, that file is returned instead of the normal page markdown You can keep the canonical docs URL and ask for markdown with an HTTP header: Browsers still receive HTML from /docs/installation; agents, scripts, and crawlers that send Accept: text/markdown receive the same machine-readable output as /docs/installation.md. See Agent Primitive for the page-level authoring model. Static export For fully static builds (e.g. Cloudflare Pages, static hosting with no server), set staticExport: true in your config. This: - Next.js: With output: "export" in next.config, the /api/docs route is not generated. The layout hides Cmd+K search and AI chat. - TanStack Start / SvelteKit / Astro / Nuxt: The layout hides the search trigger and floating AI when staticExport is true. Omit or don’t deploy the docs API route so no server is required. Use this when you deploy to a static host and don’t run a server. Search and AI require a server; with staticExport: true they are hidden so the site works without one. Search Search is enabled by default. If you do nothing, the docs API uses the built-in simple adapter with section-based chunking so heading matches feel more precise than page-level search. Use an object when you want to switch providers or tune indexing behavior: Simple search Typesense Use mode: "hybrid" together with embeddings when you want keyword + semantic retrieval. The initial built-in embeddings provider is Ollama, so you can keep that stack local or self-hosted. If you want to pre-sync the index instead of letting the first request do it lazily: Algolia You can also pre-sync Algolia outside request handling: MCP-backed search Use MCP search when you want the docs search UI to query an MCP search_docs tool instead of a native search backend. This is useful when another docs service or agent-facing MCP layer already owns retrieval and ranking. Custom adapters Notes: - search: false disables search entirely - chunking.strategy defaults to "section" and can be changed to "page" for one-document-per-page indexing - Typesense and Algolia can sync documents automatically on the first search request when adminApiKey is present - Use pnpm dlx @farming-labs/docs search sync --typesense or --algolia when you want manual indexing as a CLI step - provider: "mcp" can target a relative route like /api/docs/mcp or an absolute remote MCP endpoint - when MCP search points at the same site's relative MCP route, the MCP search_docs tool falls back to built-in simple search to avoid recursive loops - If you use a custom Next.js docs API route, import createDocsAPI from @farming-labs/next/api and pass search: docsConfig.search so your adapter config reaches the route handler - Search requires the docs API route; with staticExport: true the UI is hidden because there is no server route The older @farming-labs/theme/api import path still works, but new Next.js apps should use @farming-labs/next/api. The theme-level path is being kept for compatibility and will be deprecated. MCP Server MCP is enabled by default. Use mcp to customize the built-in MCP server for AI tools and IDE agents, or set enabled: false to opt out. Opt out explicitly: Default behavior: - HTTP route: /api/docs/mcp - stdio command: pnpx @farming-labs/docs mcp - Built-in tools: listpages, getnavigation, searchdocs, readpage Framework notes: - Next.js: withDocs() auto-generates the default /api/docs/mcp route - TanStack Start / SvelteKit / Astro / Nuxt: add the framework route file and reuse the built-in handler from the docs server helper - Custom routes: set mcp.route in docs.config and add the matching route file manually so the configured path and the actual endpoint stay aligned See the full MCP Server guide for the route snippets and stdio usage. API Reference Use apiReference to generate an API reference from either your framework route handlers or a hosted OpenAPI JSON document. Use route scanning when your API lives in the same app. Use specUrl when your backend is hosted elsewhere and already exposes an openapi.json. apiReference is supported in Next.js, TanStack Start, SvelteKit, Astro, and Nuxt. In Next.js, enabling apiReference in docs.config is enough when you use withDocs(). The API reference route is generated automatically. In TanStack Start, SvelteKit, Astro, and Nuxt, docs.config controls scanning, remote spec rendering, theming, routeRoot, and exclude, but you still need to add the framework route handler that serves /{path}. If your docs app and backend are deployed separately, set apiReference.specUrl to a hosted openapi.json. This keeps the API reference in your docs UI without requiring local route files for the actual backend. If your backend lives somewhere else, point the API reference at a hosted openapi.json instead of scanning local route files: | Property | Type | Default | Description | | ----------- | ---------- | ----------------- | ----------- | | enabled | boolean | true inside the object | Enables generated API reference pages | | path | string | "api-reference" | URL path where the generated reference lives | | specUrl | string | — | Absolute URL to a hosted OpenAPI JSON document. When set, local route scanning is skipped | | routeRoot | string | "api" | Filesystem route root to scan. Bare values like "api" resolve inside app/ or src/app/; full values like "app/internal-api" are supported too | | exclude | string[] | [] | Routes to omit from the generated reference. Accepts URL-style paths like "/api/hello" or route-root-relative entries like "hello" / "hello/route.ts" | When specUrl is set, routeRoot and exclude are ignored because the reference is rendered from the remote spec. That does not change the framework routing requirements: - Next.js still generates the API reference route automatically with withDocs() - TanStack Start, SvelteKit, Astro, and Nuxt still need the /{path} route files because those routes serve the generated API reference page If you use output: "export" in Next.js, the generated API reference route is skipped automatically because it needs a server route handler. Route conventions by framework: - Next.js: app/api//route.ts or src/app/api//route.ts - TanStack Start: src/routes/api.*.ts and nested route files under the configured route root - SvelteKit: src/routes/api//+server.ts or +server.js - Astro: src/pages/api//*.ts or .js - Nuxt: server/api//*.ts or .js Opt-in route wiring Next.js - Nothing else is required beyond apiReference in docs.config and withDocs() in next.config.ts. TanStack Start Create src/routes/api-reference.index.ts: Create src/routes/api-reference.$.ts with the same handler and createFileRoute("/api-reference/$"). SvelteKit Create src/routes/api-reference/+server.ts: Create src/routes/api-reference/[...slug]/+server.ts with the same GET export. Astro Create src/pages/api-reference/index.ts: Create src/pages/api-reference/[...slug].ts with the same GET export. Nuxt Create server/routes/api-reference/index.ts: Create server/routes/api-reference/[...slug].ts with the same default export. Changelog Use changelog to render a release feed and entry pages from dated MDX files inside your docs content tree. The turn-key generated changelog pages are currently wired in Next.js when you use withDocs(). With the default Next.js docs tree, source entries live under app/docs/changelog/YYYY-MM-DD/page.mdx. That publishes: - /docs/changelogs - /docs/changelogs/YYYY-MM-DD | Property | Type | Default | Description | | -------- | ---- | ------- | ----------- | | enabled | boolean | true inside the object | Enables generated changelog pages | | path | string | "changelog" | URL path where the changelog listing lives inside the docs layout | | contentDir | string | "changelog" | Source directory for dated entry folders like app/docs/changelog/2026-03-04/page.mdx | | title | string | "Changelog" | Listing page title | | description | string | — | Listing page description and metadata | | search | boolean | true | Show the built-in changelog search field | | actionsComponent | ReactNode \| Component | — | Custom action content rendered in the changelog rail | Useful entry frontmatter: - title - description - image - authors - version - tags - pinned - draft If you pass actionsComponent, use docs.config.tsx so JSX is valid. When you use withDocs(), the changelog route files are generated automatically. There is no separate changelog.generated.tsx file to maintain. Internationalization Use i18n to enable locale-aware docs with a language selector and query-param routing like /docs?lang=en and /docs?lang=fr. When i18n is enabled: - The docs UI renders a locale selector in the sidebar footer - Links and search navigation preserve the active lang query parameter - Each locale can have its own content tree and sidebar structure - The CLI can scaffold locale folders like docs/en and docs/fr for existing projects For content structure, place localized docs inside locale folders. For example: The generated examples and CLI scaffold use this folder structure across Next.js, TanStack Start, SvelteKit, Astro, and Nuxt. Theme Toggle Controls the light/dark mode switcher. Works on Next.js, TanStack Start, SvelteKit, Astro, and Nuxt. | Property | Type | Default | Description | | --------- | ------------------------------------- | -------------- | ---------------------------------- | | enabled | boolean | true | Show/hide the theme toggle | | default | "light" \| "dark" \| "system" | "system" | Forced theme when toggle is hidden | | mode | "light-dark" \| "light-dark-system" | "light-dark" | Toggle mode | GitHub / Edit on GitHub Enables "Edit on GitHub" links on each docs page footer. The link appears automatically when both url and directory are set. | Property | Type | Default | Description | | ----------- | -------- | -------- | ---------------------------------- | | url | string | — | Repository URL | | branch | string | "main" | Branch name | | directory | string | — | Subdirectory for the content files | Breadcrumb Controls the breadcrumb navigation above page content. Theme Configuration Pass options to your theme preset to customize colors, typography, layout, and more: Navigation The nav option controls the sidebar header: In Next.js and TanStack Start, title can also be a React element: Icons Register icons in the config and reference them in frontmatter: SvelteKit uses a built-in icon map. Add icon to your page frontmatter: Built-in icons: book, terminal, rocket, settings, shield, puzzle, zap, database, key, mail, file, folder, link, lightbulb, code, users, globe, lock. Astro uses a built-in icon map. Add icon to your page frontmatter: Built-in icons: book, terminal, rocket, settings, shield, puzzle, zap, database, key, mail, file, folder, link, lightbulb, code, users, globe, lock. Nuxt uses a built-in icon map. Add icon to your page frontmatter: Built-in icons: book, terminal, rocket, settings, shield, puzzle, zap, database, key, mail, file, folder, link, lightbulb, code, users, globe, lock. Code block copy callback Run a callback when the user clicks the copy button on a code block (in addition to the default copy-to-clipboard). Useful for analytics or logging. Next.js / TanStack Start: Set onCopyClick in defineDocs(); it is passed through to the MDX components. SvelteKit, Astro, Nuxt: Config is serialized at build time so you cannot pass a function. Use one of these: 1. Global callback — In a client-side script (e.g. in your layout), set window.fdOnCopyClick to your function. It will be called with CodeBlockCopyData after each copy. 2. Custom event — Listen for the fd:code-block-copy event on document or window; event.detail is the same { title?, content, url, language? } object. The callback receives title (if the code block has a title), content (raw code), url (current page URL), and language (syntax hint) when available. Page Feedback Show a built-in feedback prompt at the end of each docs page. The callback receives whether the user clicked the positive or negative button, plus the current page path, slug, title, URL, and locale when available. If you just want the UI without a callback, use feedback: true. Next.js / TanStack Start / SvelteKit / Nuxt: feedback.onFeedback is called from the built-in page footer UI with no extra client bridge file. Astro: feedback: true still enables the built-in UI with no extra setup. For optional client-side analytics hooks, the same payload is also emitted through window.fdOnFeedback and the fd:feedback custom event. This website enables feedback in website/docs.config.tsx and posts the callback payload to website/app/api/feedback/route.ts, where it is inserted into Prisma with the model defined in website/prisma/schema.prisma. Agent Feedback Endpoints Use feedback.agent when you want machine-readable feedback routes for coding agents or docs-aware automation. This is separate from the built-in page footer UI. Agents should discover the configured routes first with GET /.well-known/agent.json. In Next.js, GET /.well-known/agent is the fallback public alias, and GET /api/docs/agent/spec is the canonical framework route. All three return the same JSON. The spec includes site identity, locale config, capability flags, the search endpoint, markdown route pattern and Accept: text/markdown contract, API and public llms.txt routes, Skills CLI install metadata, MCP endpoint and enabled tools, and the active agent feedback schema and submit endpoints. Default behavior: - GET /.well-known/agent.json is the preferred public agent discovery URL in Next.js - GET /.well-known/agent is the public fallback discovery URL in Next.js - GET /api/docs/agent/spec returns the canonical framework discovery document - the discovery JSON advertises agentSpecDefault, agentSpecFallback, and the llms.txt defaults - GET /api/docs/agent/feedback/schema returns the feedback schema - POST /api/docs/agent/feedback accepts { context?, payload } - the shared /api/docs handler is still the source of truth - in Next.js, withDocs() adds the public rewrites automatically - feedback.agent alone does not enable the human footer UI Default request shape: Customize the public route or the payload schema when needed: Quick test: Page Actions Enable "Copy as Markdown" and "Open in LLM" buttons: Use openDocs: true for the built-in ChatGPT + Claude provider list, or pass providers to fully customize it. See the full Page Actions reference for all options. Ask AI Add a RAG-powered AI chat that lets users ask questions about your docs: The API key is read from process.env.OPENAIAPIKEY automatically. Pass the API key through src/lib/docs.server.ts: Pass the API key through docs.server.ts (SvelteKit requires server-only env access): Pass the API key through docs.server.ts (Astro requires server-only env access): Pass the API key through the server handler (Nuxt requires server-only env access): See the full Ask AI reference for all options including custom providers, labels, system prompts, and more. Full Example --- ## Contributing URL: https://docs.farming-labs.dev/docs/contributing How to contribute to @farming-labs/docs Contributing Thanks for your interest in contributing to @farming-labs/docs. This guide explains how to report issues, propose changes, and submit pull requests. Code of conduct Please be respectful and constructive. We aim to keep the community welcoming and focused on building a great documentation framework. How to contribute Reporting bugs - GitHub Issues — Open an issue at github.com/farming-labs/docs/issues. - Include: - Clear title and description - Steps to reproduce - Expected vs actual behavior - Your environment (Node version, package manager, framework: Next.js / TanStack Start / SvelteKit / Astro / Nuxt) - Relevant config or code snippets Suggesting features - Open a discussion or issue describing the use case and proposed behavior. - Check existing issues first to avoid duplicates. Pull requests 1. Fork the farming-labs/docs repo and clone it locally. 2. Branch from main (e.g. fix/typo-install-docs or feat/contributing-guide). 3. Install dependencies from the repo root: 4. Build the monorepo (optional, for core/theme changes): 5. Run the docs site (from repo root): Then open http://localhost:3000 to preview. 6. Make your changes — Keep edits focused; doc-only and small fixes are welcome. 7. Commit with a clear message (e.g. docs: add contributing guide, fix: correct CLI flag in installation). 8. Push your branch and open a Pull Request against main. 9. Describe what you changed and why; link any related issues. Project structure - packages/ — Core packages: docs, fumadocs (theme), next, svelte, svelte-theme, astro, astro-theme, nuxt, nuxt-theme. - website/ — This docs site (Next.js); content under app/docs/. - examples/ — Example apps (Next.js, TanStack Start, and others) for testing. When you work on this monorepo, keep local @farming-labs/ example dependencies linked with workspace: so CI and Vercel on Node 22 resolve the built workspace adapters instead of an older published package from node_modules. Doc content lives in website/app/docs/ as MDX. Config and sidebar are driven by docs.config.tsx in website/. Documentation changes - Edit or add .mdx files under website/app/docs/. - Use existing frontmatter (title, description, icon, order) so the sidebar stays consistent. - Use the shared `, , and ` components where they fit. Questions - GitHub Discussions — For general questions and ideas, use github.com/farming-labs/docs/discussions. - GitHub Issues — You can still use Issues for bugs and actionable feature requests. - Issues — For bugs and concrete feature requests. Thank you for contributing. --- ## Customization URL: https://docs.farming-labs.dev/docs/customization Colors, typography, sidebar, components, agent primitives, OG images, AI chat, and page actions Customization Everything is customizable from docs.config.ts. No CSS files to edit, no layout files to create. What You Can Customize - Colors — Primary, accent, background, border, and more - Typography — Font families, heading sizes, weights, spacing - Sidebar — Title, icons, collapsible groups, style - Components — Override built-in or add custom MDX components, including live HoverLink previews - Agent Primitive — Built-in ` blocks for additive machine context and sibling agent.md files for full page overrides - OG Images — Dynamic or static Open Graph images; what context your image generator receives; how the docs website does it - Ask AI — RAG-powered AI chat with configurable LLM, floating/search modes, and more - Page Actions — "Copy Markdown" and "Open in LLM" buttons on doc pages - MCP Server — Built-in MCP tools/resources over stdio and /api/docs/mcp - llms.txt — Auto-generate llms.txt and llms-full.txt for LLM-friendly documentation All customization happens through the theme and top-level config options in docs.config.ts`. --- ## Agent Primitive URL: https://docs.farming-labs.dev/docs/customization/agent-primitive Author page-level agent context with the built-in Agent block or a sibling agent.md override. Agent Primitive @farming-labs/docs gives you two page-level primitives for agent-facing documentation: - ... inside page.mdx for additive machine-only context - agent.md beside page.mdx for a full machine-readable override The same page model also powers the built-in machine-readable routes: - the shared docs API supports GET /api/docs?format=markdown&path= - in Next.js, withDocs() also serves /docs/.md - in Next.js, Accept: text/markdown on /docs/ returns the same markdown - agents can discover the configured contract with GET /.well-known/agent.json, fall back to GET /.well-known/agent, or use the canonical framework route at GET /api/docs/agent/spec /docs/installation stays the human HTML page by default. Send Accept: text/markdown to that same URL when an agent or script wants the machine-readable version without appending .md. Use Agent when the human page is already mostly correct and just needs a little extra implementation context. Use agent.md when the entire machine-readable page should be more operational, more structured, or fully different from the visible UI page. Agent is already part of the default MDX component map. You can write ` directly in any docs page without adding it to docs.config.ts, mdx-components.tsx, or a custom components map. If you intentionally build your own MDX map from scratch, import it from @farming-labs/theme or @farming-labs/theme/mdx. Use Agent For Additive Context Agent is the lightweight option. It keeps the normal page clean while adding agent-only guidance to the .md route and MCP fallback for that page. That gives you: - /docs/installation for the normal UI page - /docs/installation.md for machine-readable markdown that includes the Agent block - /docs/installation with Accept: text/markdown for the same machine-readable markdown - MCP read_page("/docs/installation") with the same page-level machine context Bootstrap Agents With The Spec Use the docs landing page or another high-traffic route to tell implementation agents how to discover the machine-readable surface before they start reading individual pages. No setup is required first; the default runtime already understands the Agent primitive. This works well on /docs because many agents start there first. The instruction stays hidden from the human UI, but it appears when an agent reads /docs.md, requests /docs with Accept: text/markdown, or reads the page through MCP. Use agent.md For A Full Override When the machine-readable page should be completely different, add a sibling agent.md. In that setup: - /docs/customization/agent-primitive renders page.mdx - /docs/customization/agent-primitive.md returns agent.md - MCP read_page("/docs/customization/agent-primitive") also prefers agent.md This is the stronger option. Use it when agents need: - exact file paths and command ordering - implementation-only constraints - testing or validation steps - a shorter, more targeted page than the human docs should show Point Agents At Feedback Endpoints Page primitives are also a good place to tell agents how to report whether the docs were enough to complete a task. If feedback.agent is enabled in docs.config, agents can read the schema first and then post structured feedback back into the shared docs API: - schema: /api/docs/agent/feedback/schema - submit: /api/docs/agent/feedback Example additive block: That works especially well when: - the feedback instruction belongs to one page or workflow - you want the agent-facing .md route to carry the reporting contract - the human page should stay uncluttered Agent Discovery GET /.well-known/agent.json is the preferred public discovery URL. GET /.well-known/agent is the public fallback, and GET /api/docs/agent/spec is the canonical framework route. In Next.js, all three return the same small JSON document generated from docs.config. Agents should fetch it before choosing a transport. It tells them: - which docs site and entry they are reading - which locales are configured and which query parameter selects one - which agent-facing capabilities are enabled - where the shared docs API lives - which agent discovery URL is the default and which public route to use as fallback - where to search the docs with a query - which markdown URL pattern to use for page reads - where to fetch llms.txt and llms-full.txt content, including the default public URLs and the public /.well-known aliases when available - how to install the published Skills pack and which skill is recommended first - whether MCP is enabled, which endpoint to call, and which tools are available - whether agent feedback is enabled, plus the schema and submit endpoints to use For Next.js apps, withDocs() wires /.well-known/agent.json, /.well-known/agent, and /api/docs/agent/spec into the same shared /api/docs handler automatically. The generated spec also includes agentSpecDefault: "/.well-known/agent.json" and agentSpecFallback: "/.well-known/agent" so downstream integrations do not need to hard-code the preference themselves. Choosing Between Them - Use Agent when the normal page should stay the source of truth and you only need extra machine context - Use agent.md when the machine-readable page should become its own focused document - Use both patterns page-by-page; you do not need a global config flag to turn them on Page Primitives Vs Skills Agent and agent.md are page-scoped primitives. They are the right fit when the machine context belongs to a single docs route. Use an installable skill when the guidance should be reusable across many pages or workflows, such as setup, CLI usage, theme creation, page actions, or deeper configuration tasks. In practice, the two features work well together: - use Agent or agent.md to make one page more useful to agents - use skills when the knowledge should be bundled as a reusable workflow outside a single page This repo already publishes skills under skills/farming-labs/. Install them with: Common choices include: - getting-started for setup and installation - cli for init, upgrade, and CLI flags - configuration for docs.config options - page-actions` for Copy Markdown and Open in LLM behavior Use page primitives for route-level context. Use skills for repeatable product knowledge that should travel across tasks. --- ## Ask AI URL: https://docs.farming-labs.dev/docs/customization/ai-chat Add a RAG-powered AI chat to your documentation Ask AI Add a built-in AI chat that lets users ask questions about your documentation. The AI searches relevant pages, builds context, and streams a response from any OpenAI-compatible LLM. Quick Start That's it. The AI reads your OPENAIAPIKEY environment variable and uses gpt-4o-mini by default. Add OPENAIAPIKEY to your .env file: The key is automatically read from process.env.OPENAIAPIKEY. Add OPENAIAPIKEY to your .env file: Pass it through src/lib/docs.server.ts: Add OPENAIAPIKEY to your .env file: Pass it through docs.server.ts (SvelteKit requires server-only env access): Add OPENAIAPIKEY to your .env file: Pass it through docs.server.ts: Add OPENAIAPIKEY to your .env file: Nuxt automatically reads environment variables via Nitro's runtime config. The defineDocsHandler reads process.env.OPENAIAPIKEY on the server. Configuration Reference All options go inside the ai object in docs.config.ts: enabled Whether to enable AI chat functionality. | Type | Default | | --------- | ------- | | boolean | false | mode How the AI chat UI is presented. | Type | Default | | ------------------------ | ---------- | | "search" \| "floating" | "search" | - "search" — AI tab integrated into the Cmd+K search dialog. Users switch between "Search" and "AI" tabs. - "floating" — A floating chat widget with a button on screen. Opens as a panel, modal, or full-screen overlay. position Position of the floating chat button on screen. Only used when mode is "floating". | Type | Default | | ---------------------------------------------------- | ---------------- | | "bottom-right" \| "bottom-left" \| "bottom-center" | "bottom-right" | floatingStyle Visual style of the floating chat when opened. Only used when mode is "floating". | Type | Default | | ------------------------------------------------- | --------- | | "panel" \| "modal" \| "popover" \| "full-modal" | "panel" | - "panel" — A tall panel that slides up from the button position. No backdrop overlay. - "modal" — A centered modal dialog with a backdrop overlay, similar to the Cmd+K search dialog. - "popover" — A compact popover near the button. Suitable for quick questions. - "full-modal" — A full-screen immersive overlay. Messages scroll in the center, input is pinned at the bottom, suggested questions appear as horizontal pills. model The LLM model configuration. Can be a simple string (single model) or an object with multiple selectable models. Simple — single model: | Type | Default | | -------- | --------------- | | string | "gpt-4o-mini" | Advanced — multiple models with UI dropdown: | Type | Default | | -------- | ------- | | object | — | Each model entry has: - id — The model identifier sent to the LLM API (e.g. "gpt-4o-mini") - label — Display name shown in the UI dropdown (e.g. "GPT-4o mini (fast)") - provider — (optional) Key matching a named provider in the providers config. If omitted, uses the default baseUrl and apiKey. When model is an object with a models array, a model selector dropdown appears in the AI chat interface so users can pick which model to use. providers Named provider configurations. Each provider has its own baseUrl and apiKey, allowing models from different providers to coexist in a single config. | Type | Default | | -------- | ------- | | object | — | When a user selects a model in the dropdown, the backend automatically uses that model's provider to resolve the correct baseUrl and apiKey. All providers must be OpenAI Chat Completions API compatible (OpenAI, Groq, Together, Fireworks, OpenRouter, Ollama, any vLLM deployment). baseUrl Default base URL for an OpenAI-compatible API endpoint. Used when no per-model provider is configured. | Type | Default | | -------- | ----------------------------- | | string | "https://api.openai.com/v1" | apiKey Default API key for the LLM provider. Used when no per-model provider is configured. Falls back to process.env.OPENAIAPIKEY if not set. | Type | Default | | -------- | ---------------------------- | | string | process.env.OPENAIAPIKEY | Warning: Never hardcode API keys. Always use environment variables. systemPrompt Custom system prompt prepended to the AI conversation. Documentation context is automatically appended after this prompt. | Type | Default | | -------- | ------------------------------------------------ | | string | "You are a helpful documentation assistant..." | maxResults Maximum number of search results to include as context for the AI. More results = more context but higher token usage. | Type | Default | | -------- | ------- | | number | 5 | suggestedQuestions Pre-filled suggested questions shown in the AI chat when the conversation is empty. Clicking one fills the input and submits automatically. | Type | Default | | ---------- | ------- | | string[] | [] | aiLabel Display name for the AI assistant in the chat UI. Shown as the message label and header title. | Type | Default | | -------- | ------- | | string | "AI" | packageName The npm package name used in code examples. The AI will use this in import snippets instead of generic placeholders. | Type | Default | | -------- | ------- | | string | — | docsUrl The public URL of your documentation site. The AI will use this for absolute links instead of relative paths. | Type | Default | | -------- | ------- | | string | — | loader Loading indicator variant shown while the AI generates a response. | Type | Default | | -------- | ---------------- | | string | "shimmer-dots" | Available variants: "shimmer-dots", "circular", "dots", "typing", "wave", "bars", "pulse", "pulse-dot", "terminal", "text-blink", "text-shimmer", "loading-dots". loadingComponent Custom React component that completely overrides the built-in loader variant. Receives { name } (the aiLabel value). Only works in Next.js — for other frameworks, use the loader option. | Type | Default | | ------------------------------------------ | ------- | | (props: { name: string }) => ReactNode | — | triggerComponent Custom trigger button for the floating chat. Replaces the default sparkles button. Only used when mode is "floating". Each framework accepts its native component format — pass it as a prop on DocsLayout (or a slot in Astro). | Type | Default | | ----------- | ------------------------ | | Component | Built-in sparkles button | Pass a React component via docs.config.tsx: Import a Svelte component and pass it as a prop on DocsLayout: Use the trigger-component slot on DocsLayout: Import a Vue component and pass it as a prop on DocsLayout: Full Example — Single Provider Full Example — Multiple Providers Users see a model dropdown in the AI chat interface. When they pick a model, the backend automatically routes the request to the correct provider's API with the right credentials. Using a Different LLM Provider Single provider (simple) Use any OpenAI-compatible API by setting baseUrl and model: Multiple providers Use the providers map to configure multiple APIs, then reference them from each model entry: Compatible providers: OpenAI, Groq, Together AI, Fireworks, OpenRouter, Azure OpenAI, Ollama (local), any vLLM deployment — anything that speaks the OpenAI Chat Completions API format. --- ## Colors URL: https://docs.farming-labs.dev/docs/customization/colors Override any color token from config Colors Override any color token directly from your config. Colors are mapped to --color-fd-* CSS variables at runtime. Setting Colors Available Tokens | Token | CSS Variable | Description | | ------------------- | ------------------------------- | --------------------------- | | primary | --color-fd-primary | Primary brand color | | primaryForeground | --color-fd-primary-foreground | Text on primary backgrounds | | background | --color-fd-background | Page background | | foreground | --color-fd-foreground | Default text color | | muted | --color-fd-muted | Muted background | | mutedForeground | --color-fd-muted-foreground | Muted text | | border | --color-fd-border | Border color | | card | --color-fd-card | Card background | | accent | --color-fd-accent | Accent background | | accentForeground | --color-fd-accent-foreground | Text on accent | | secondary | --color-fd-secondary | Secondary background | | ring | --color-fd-ring | Focus ring color | How It Works Only colors you explicitly set are emitted as inline CSS variables. Theme preset defaults stay in the CSS file — your overrides don't break light/dark mode handling. Color Formats Any valid CSS color value works: --- ## Components URL: https://docs.farming-labs.dev/docs/customization/components Override built-in or add custom MDX components Components Pass custom React components that become available in all MDX files — no imports needed. How It Works When you register components in docs.config.ts, they're merged into the MDX component map via getMDXComponents(). The merge order is: 1. Built-in components — headings, code blocks, callouts, Tab, Tabs, HoverLink, etc. 2. Theme defaults — from theme.ui.components for built-ins like HoverLink 3. Your components — from docs.config.ts (overrides built-ins if names match) This means you can both add new components and override existing ones. Creating a Custom Component 1. Create the component 2. Register in config 3. Use in any MDX file No import needed — just use it directly: Overriding Built-in Components You can replace any built-in MDX component by registering one with the same name. Your component will take precedence. Available built-in components The following components are available by default (from fumadocs-ui): - h1, h2, h3, h4, h5, h6 — heading elements - p, a, ul, ol, li — text and list elements - table, thead, tbody, tr, th, td — table elements - pre, code — code blocks and inline code - img — images - blockquote — blockquotes - hr — horizontal rules - HoverLink — hover-triggered popover link card with title, description, and CTA - Tab, Tabs — tabbed content - Callout — callout/admonition boxes Example: Use HoverLink HoverLink gives you an inline trigger that opens a richer popover card on hover or focus. It is useful when you want to reference another page without breaking the reader's flow immediately. Live example: installation guide Theme defaults vs config overrides Use theme.ui.components when you want to keep the built-in HoverLink but change its default props across the site. Use components.HoverLink when you want to replace the component entirely. Example: Override the a tag Now every link in your MDX files will use CustomLink — external links open in a new tab with an arrow indicator. Example: Override blockquote Example: Override pre (code blocks) Multiple Components Register as many components as you need: Then use them all across your MDX without imports: Component Props Components receive their props as defined in your implementation. The only requirement is that they must be valid React components (function or class). For built-in overrides, your component should accept the same props as the original element (e.g., React.AnchorHTMLAttributes for a, React.HTMLAttributes for pre). For custom components, define whatever props interface you need. --- ## llms.txt URL: https://docs.farming-labs.dev/docs/customization/llms-txt Auto-generate llms.txt and llms-full.txt for LLM-friendly documentation llms.txt Serve llms.txt content through your existing docs API — no extra route files needed. Just enable it in your config. What is llms.txt? llms.txt is a standard for making website content accessible to LLMs. In Next.js, withDocs() serves conventional public aliases that rewrite to your existing /api/docs endpoint: - /llms.txt — The default concise markdown listing of all pages with titles, URLs, and descriptions - /llms-full.txt — The default full stripped content of every page, ready for LLM consumption - /.well-known/llms.txt — Alias for /llms.txt - /.well-known/llms-full.txt — Alias for /llms-full.txt The shared API handler remains the source of truth: - /api/docs?format=llms — A concise markdown listing of all pages with titles, URLs, and descriptions - /api/docs?format=llms-full — The full stripped content of every page, ready for LLM consumption Quick Start That's it. The existing API handler serves the content automatically — no extra route files needed. Configuration Reference All options go inside the llmsTxt object in docs.config.ts: llmsTxt.enabled Enable or disable llms.txt generation. | Type | Default | | --------- | ------- | | boolean | false | llmsTxt.baseUrl Base URL prepended to all page links in the generated files. | Type | Default | | -------- | ------- | | string | "" | llmsTxt.siteTitle Title shown at the top of the generated files. Falls back to nav.title if not set. | Type | Default | | -------- | --------------- | | string | nav.title | llmsTxt.siteDescription Description shown below the title. | Type | Default | | -------- | ----------- | | string | undefined | How It Works No extra route files are needed. The existing API handler (/api/docs on Next.js, or your framework's equivalent) serves the llms.txt content when you pass the format query parameter: - GET /api/docs?format=llms — concise page listing - GET /api/docs?format=llms-full — full page content In Next.js, withDocs() also adds the crawler-friendly public aliases automatically: - GET /llms.txt — default concise route - GET /llms-full.txt — default full-content route - GET /.well-known/llms.txt - GET /.well-known/llms-full.txt - GET /.well-known/agent.json for the preferred agent discovery spec that references those llms.txt routes - GET /.well-known/agent as the fallback agent discovery alias Those aliases rewrite to the same shared API output, so there is still only one content pipeline. The agent discovery spec also advertises defaultTxt: "/llms.txt" and defaultFull: "/llms-full.txt" so integrations can use the intended defaults without adding their own framework-specific routes. TanStack Start, SvelteKit, Astro, and Nuxt can use the shared API query routes directly. Output Example /api/docs?format=llms /api/docs?format=llms-full Footer Links When enabled, llms.txt and llms-full.txt links automatically appear in the page footer next to "Edit on GitHub", pointing to the public /llms.txt and /llms-full.txt routes. Full Example --- ## MCP Server URL: https://docs.farming-labs.dev/docs/customization/mcp Expose your docs as MCP tools and resources over stdio or /api/docs/mcp MCP Server @farming-labs/docs can expose your docs as a built-in MCP server, so AI clients can search the docs, read pages, and inspect the nav tree without scraping HTML. The current built-in surface includes: - list_pages - get_navigation - search_docs - read_page It also exposes resources for: - docs://navigation - one docs://… resource per page Default behavior MCP is enabled by default. That gives you the built-in MCP surface with the default Streamable HTTP route: The hosted docs site has MCP enabled at https://docs.farming-labs.dev/api/docs/mcp. Opt out explicitly: Default HTTP route /api/docs/mcp is the default MCP endpoint across the framework adapters. Next.js auto-generates the default /api/docs/mcp route when you use withDocs(), unless you explicitly disable MCP. TanStack Start, SvelteKit, Astro, and Nuxt still need the framework route file, but they all reuse the built-in MCP handler from the docs server helper. Next.js With withDocs(), no extra route file is needed for the default path. TanStack Start SvelteKit Your src/lib/docs.server.ts can keep using the normal helper: Astro Nuxt Custom route If you want a custom MCP route, set it in docs.config and add the route file yourself. The package only auto-generates the default Next.js route at /api/docs/mcp. If you choose a custom route, keep that path in docs.config and add the matching route file manually so the app and the MCP client point at the same endpoint. Example custom Next.js route: @farming-labs/theme/api still works if you already rely on it, but prefer @farming-labs/next/api for Next.js routes going forward. The theme-level path will be deprecated. When search is configured, the MCP search_docs tool uses that same adapter pipeline too. That means your docs UI search and MCP search can share the same provider, chunking strategy, and custom ranking logic instead of drifting apart. That also means you can flip docs search itself over to MCP: For local self-hosted setups, relative MCP endpoints like /api/docs/mcp are supported. The built-in search_docs tool automatically falls back to simple search internally when it detects that same-route loop, so the route stays usable for testing and local examples. Stdio transport The same docs graph is available locally over stdio: Or with pnpm in an installed project: By default the CLI reads docs.config.ts[x] from the project root. If your config lives somewhere else: Use the hosted MCP endpoint If you just want to try the live docs server, you can point your MCP client at: More setup docs - Cursor MCP docs - VS Code MCP docs - Claude Code MCP docs Manual setup Cursor project or global config: VS Code workspace config: Claude Code: If you are connecting to a local Next dev server, use http://127.0.0.1:3000/api/docs/mcp. Using https://localhost:3000/... against a non-TLS dev server will fail with SSL errors. Test the Next example This repo's Next example exposes the default MCP endpoint directly. Start the example: Then connect your MCP client or inspector to: The built-in HTTP route exposes the full MCP surface: - list_pages - get_navigation - search_docs - read_page What to ask it Once your MCP client is connected, these are good first checks: - search_docs for feedback, page actions, or mcp - read_page for /docs/configuration or installation - get_navigation to inspect the docs tree - list_pages to confirm the server is loading the expected docs set In Cursor or VS Code, natural prompts like these work well too: - Search the Farming Labs docs for page feedback setup - Read the configuration page and summarize MCP config - Find the page actions docs and tell me how openDocs works What the tools return - list_pages returns page titles, slugs, and URLs - get_navigation returns the docs tree in a readable text outline - search_docs ranks pages by title, description, and content matches - read_page accepts either a slug like installation or a full docs path like /docs/installation The built-in resources mirror that same data: - docs://navigation - docs://docs - docs://docs/installation - docs://docs/guides/quickstart When to use this vs markdown routes and llms.txt - Use markdown routes when you want a plain HTTP URL for one page - Use llms.txt when you want a static, crawler-friendly docs summary - Use MCP when you want a structured, queryable docs interface for agents and IDE tools They complement each other well. Markdown routes are the simplest page-level HTTP surface, llms.txt is lightweight and public, and MCP is interactive and tool-based. --- ## OG Images URL: https://docs.farming-labs.dev/docs/customization/og-images Dynamic and static Open Graph images for docs — how they work, what context is passed, and how the docs website generates them OG Images Open Graph (OG) images are the preview images shown when your docs pages are shared on social platforms, Slack, or messaging apps. This guide explains how the framework supports dynamic (generated per page) and static (per-page image file) OG images, what data your image generator receives, and how the docs website implements dynamic OG. Overview: static vs dynamic One design for all pages; title and description change per page. You implement an API route. The framework calls it with the page’s title and description (from frontmatter) as query params and uses the returned image URL for og:image and twitter:image. Custom image per page (e.g. diagrams, screenshots). Set openGraph and/or twitter (or the shorthand ogImage) in that page’s frontmatter. The framework uses those values instead of calling the dynamic endpoint. You can mix both: use a dynamic endpoint by default and override with static images on specific pages via frontmatter. How dynamic OG works 1. You configure an OG endpoint in docs.config.ts: - og: { enabled: true, type: "dynamic", endpoint: "/api/og" } 2. For each docs page, the framework builds a single URL that includes the page’s context: - Next.js (MDX): A remark plugin injects that URL into the page’s exported metadata (from frontmatter). The URL is your endpoint plus query params. - Query params the framework adds: - title — the page’s title from frontmatter (required). - description — the page’s description from frontmatter (optional). 3. Your API route receives a GET request with those query params and returns a 1200×630 image (e.g. PNG). 4. The framework sets that URL as og:image and twitter:image in the page metadata, so crawlers and social platforms fetch the image when the page is shared. So your image generator gets full context: the same title and description that appear in the doc and in meta tags. You can render them (and any other logic) inside your image. What context your image generator receives When the framework calls your dynamic OG endpoint, it passes: | Query param | Source | Example value | | ------------- | ----------------- | -------------------- | | title | Page frontmatter title | Introduction or OG Images | | description | Page frontmatter description | Dynamic and static Open Graph images for docs. | The URL looks like: Your route reads searchParams.get("title") and searchParams.get("description") and uses them when rendering the image (e.g. in ImageResponse). That’s the full context the framework provides; you can add more query params in your own links if needed. Example: how the docs website uses dynamic OG The @farming-labs/docs website which uses Nextjs uses a dynamic OG endpoint so every docs page gets a unique preview image with that page’s title and description. 1. Config (docs.config.ts) No other OG config is required; the framework builds the URL for each page and injects it into metadata. 2. API route (app/api/og/route.ts) The route receives title and description from the query string and returns a 1200×630 image using Next.js ImageResponse: So the image generator has full context: the same title and description that come from the MDX frontmatter. The live site loads Inter and JetBrains Mono for typography and uses the same layout with diagonal pattern and borders; the only variables are title and description. 3. What each page gets - Introduction (/docs) → /api/og?title=Introduction&description=... - OG Images (/docs/customization/og-images) → /api/og?title=OG+Images&description=Dynamic+and+static+... - API Reference (/docs/reference) → /api/og?title=API+Reference&description=... Each URL is set as that page’s og:image and twitter:image, so shares show the correct title and description on the card. Using static OG for specific pages To use a static image for a single page (e.g. a pre-made graphic or screenshot), set openGraph and twitter in that page’s frontmatter. The framework will use those instead of calling the dynamic endpoint for that page. Shorthand: you can set only ogImage: "/og/some-page.png" and the framework will use it for both Open Graph and Twitter unless you override with full openGraph / twitter. See Page Frontmatter for all options. Configuration reference | Option | Type | Description | | ------------- | -------- | ----------- | | og.enabled | boolean| Turn OG/twitter images on or off. | | og.type | "static" \| "dynamic" | Use a fixed image or a dynamic endpoint. | | og.endpoint | string| Path to your OG API route (e.g. "/api/og"). Required for dynamic. | | og.defaultImage | string | Fallback OG image URL when no page-specific OG is set. | Full details: OGConfig in the API reference. Testing your OG image 1. Start your dev server (e.g. pnpm dev). 2. Open the endpoint with sample params in the browser: - http://localhost:3000/api/og?title=Test+Page&description=My+description 3. Confirm the image shows the right title and description. 4. After code changes, do a hard refresh (e.g. Cmd+Shift+R) or use a cache-busting query (e.g. &_=1) so you see the latest image. Summary - Dynamic OG gives every docs page a generated preview: the framework calls your endpoint with title and description from frontmatter, and you return a 1200×630 image. The docs website uses this with a single /api/og route. - Static OG lets you set a specific image per page via frontmatter (openGraph, twitter, or ogImage), which overrides the dynamic endpoint for that page. - Your image generator receives the full context it needs: the page’s title and description as query parameters, so you can render them (and anything else you derive from them) in your image. --- ## Page Actions URL: https://docs.farming-labs.dev/docs/customization/page-actions Add "Open in LLM" and "Copy Markdown" buttons to your doc pages Page Actions Page actions are buttons rendered above or below the page title. They let users interact with page content — copy it as Markdown, or open it in an LLM provider like ChatGPT or Claude. Quick Start This adds two buttons: Copy Markdown and an Open in... dropdown. Configuration Reference All options go inside the pageActions object in docs.config.ts: pageActions.position Where to render the page action buttons relative to the page title. | Type | Default | | -------------------------------- | --------------- | | "above-title" \| "below-title" | "below-title" | pageActions.alignment Control whether the action row is left- or right-aligned. | Type | Default | | --------------------- | -------- | | "left" \| "right" | "left" | Copy Markdown pageActions.copyMarkdown Whether to show the "Copy Markdown" button. Copies the current page's content as Markdown to the clipboard. | Type | Default | | --------------------------------- | ------- | | boolean \| { enabled: boolean } | false | Open in LLM The Open in... dropdown lets users send the current page to an LLM or tool. Each provider gets a link that includes the page URL as context. pageActions.openDocs Enable the "Open in..." dropdown. Can be a boolean or an object with additional configuration. | Type | Default | | --------------------------- | ------- | | boolean \| OpenDocsConfig | false | When set to true, the built-in provider list is used: ChatGPT and Claude. pageActions.openDocs.enabled Whether to show the "Open in..." dropdown. | Type | Default | | --------- | ------- | | boolean | false | pageActions.openDocs.providers Custom list of LLM / tool providers to show in the dropdown. Overrides the default list. | Type | Default | | -------------------- | ----------------- | | OpenDocsProvider[] | Built-in defaults | Each provider has the following shape: {githubUrl} only resolves when the top-level github config is set in defineDocs(). Provider URL Templates The urlTemplate string supports these placeholders: | Placeholder | Replaced with | | ------------- | ----------------------------------------------------------------------------- | | {url} | The current page URL (e.g. https://docs.example.com/docs/installation) | | {mdxUrl} | The raw .mdx source URL for the page (useful when you want the source file) | | {githubUrl} | GitHub edit URL for the current page (same as "Edit on GitHub"). Requires github in config. Use urlTemplate: "{githubUrl}" so "Open in GitHub" opens the file on GitHub. | {mdxUrl} points at the raw source file. If you want the built-in machine-readable docs page instead, use {url}.md. In Next.js, that route can return a sibling agent.md when the page has one. HTTP clients can also request the normal page URL with Accept: text/markdown for the same response. See Agent Primitive. Custom Providers Example Example using the public markdown route instead of raw source: Cursor deeplink: The example uses the web format (https://cursor.com/link/prompt?text=...). To open the Cursor app directly instead, use the app scheme: cursor://anysphere.cursor-deeplink/prompt?text=Read+this+documentation:+{url}. With Icons (Next.js) In Next.js, you can pass React elements for icons: Full Example --- ## Sidebar URL: https://docs.farming-labs.dev/docs/customization/sidebar Title, icons, collapsible groups, and styling Sidebar The sidebar is auto-generated from your file structure. Customize its appearance through config. Custom Title The sidebar title supports strings or React components: Icons Add icons to sidebar items via frontmatter: Register icons in your config: Collapsible Groups Nested directories automatically become collapsible groups. A directory with its own page.mdx becomes a folder with an index page: Flat Mode Render all sidebar items without collapsible sections (Mintlify-style): Sidebar Header & Footer Add custom content above and below the navigation items using banner and footer: The banner renders above the navigation items and footer renders below them, both inheriting the theme's sidebar styling automatically. Next.js and TanStack Start can pass React nodes directly in docs.config.tsx. For non-React frameworks, use named slots: Pass sidebarHeader and sidebarFooter snippets: Use sidebar-header and sidebar-footer named slots: Use #sidebar-header and #sidebar-footer slots: Custom Sidebar Component Replace the entire sidebar navigation with your own component. The component receives the full page tree with all parent-child relationships. Pass a render function via sidebar.component in your config: Your component receives the full tree structure: Pass a render function via sidebar.component in your config: Your component can read the current route with TanStack Router: Use the sidebar snippet on `. It receives tree and isActive: Use the sidebar named slot: Use the #sidebar scoped slot: Tree Structure The tree prop has this shape: All types are exported from @farming-labs/docs for full type safety. Sidebar Style Breadcrumbs Enable breadcrumb navigation: Shows Parent / Current Page` with the parent being clickable. --- ## Typography URL: https://docs.farming-labs.dev/docs/customization/typography Fonts, heading sizes, weights, and spacing Typography Control fonts, heading sizes, weights, line heights, and letter spacing — all from config. Font Families Heading Styles Each heading level can be individually configured: Font Style Properties | Property | Type | Description | | --------------- | -------- | --------------------------------- | | size | string | CSS font-size (e.g. "2rem") | | weight | number | Font weight (e.g. 700) | | lineHeight | string | Line height (e.g. "1.2") | | letterSpacing | string | Letter spacing (e.g. "-0.02em") | Using Google Fonts Load fonts in your app/layout.tsx and reference them via CSS variables: Then reference them in your typography config: --- ## Installation URL: https://docs.farming-labs.dev/docs/installation Get up and running in minutes Installation Option A: CLI (Recommended) Starting from scratch? Use --template with --name to bootstrap a new docs project (Next.js, TanStack Start, Nuxt, SvelteKit, or Astro). No prompts — the CLI creates the project folder and runs install. See the --template section below. Adding docs to an existing app? Run the init command inside your Next.js, TanStack Start, SvelteKit, Astro, or Nuxt project: The CLI will first ask: Are you adding docs to an existing project or starting fresh? Choose Existing project to add docs to the current directory. It will then: 1. Detect your framework (Next.js, TanStack Start, SvelteKit, Astro, or Nuxt), or ask you to pick one 2. Ask you to pick a theme (including Create your own theme, which prompts for a theme name and scaffolds themes/.ts and themes/.css) 3. Ask about path aliases and which global CSS file to use (defaults are offered; press Enter to accept) 4. Ask for the docs entry path (default: docs — press Enter to accept) 5. Optionally scaffold i18n by selecting locales, adding custom locale codes, and choosing a default locale TanStack Start currently skips the built-in i18n scaffold so the generated routes stay minimal and working 6. Generate config, layout, CSS, and sample pages 7. Install dependencies and optionally start the dev server Prompts that show a placeholder (e.g. entry path, theme name) use that value as the default — you can press Enter to skip typing. See CLI — Init flow for the full sequence. If you enable i18n, the CLI creates locale folders like docs/en and docs/fr, writes the i18n block to docs.config, and scaffolds the framework-specific files needed for routes like /docs?lang=en. To upgrade all docs packages to the latest later, run from your project root: npx @farming-labs/docs upgrade (framework is auto-detected). See CLI — Upgrade for options. Starting from scratch or want a full example? Use --template with --name to bootstrap a ready-to-run docs app. The CLI creates the project folder, bootstraps it with the chosen framework, and runs install: Replace my-docs with your project name. Then run cd my-docs and start the dev server (npm run dev, pnpm run dev, yarn dev, or bun run dev). Option B: Manual Setup For each framework below, add your theme's CSS to your global stylesheet (e.g. app/global.css, src/app.css, or nuxt.config). The import path must match the theme you use in docs.config — e.g. default, greentree, darksharp. Without it, docs pages will lack the correct styling. ### 1. Install packages > Monorepo note: If you are developing @farming-labs/docs and a TanStack Start app in the same workspace, keep @farming-labs/docs, @farming-labs/theme, and @farming-labs/tanstack-start linked locally (for example workspace:*). This avoids Node 22 / Vercel trying to load raw adapter TypeScript from node_modules. ### 2. Create docs.config.tsx ### 3. Create next.config.ts ### 4. Set up RootProvider in app/layout.tsx Wrap your app with RootProvider from @farming-labs/theme. This enables search, theme switching, and AI chat across all pages. > RootProvider automatically configures the search API endpoint (/api/docs) and handles theme persistence. The suppressHydrationWarning on ` prevents React warnings from the theme class injection. ### 5. Import theme CSS in app/global.css Add your theme's CSS to your global stylesheet so docs styling applies. Use the import that matches the theme in your config (e.g. default below; for greentree use @farming-labs/theme/greentree/css). ### 6. Write your first doc Create an MDX file under app/docs/: Each page uses frontmatter for metadata: That's it — the docs layout (app/docs/layout.tsx) is auto-generated by withDocs(). The framework handles routing, MDX compilation, and metadata from your config. > See CLI for all generated files including the full app/layout.tsx and app/docs/layout.tsx, or check Configuration for all available options. ### 1. Install packages > Monorepo note: If you are developing @farming-labs/docs and a TanStack Start app in the same workspace, keep @farming-labs/docs, @farming-labs/theme, and @farming-labs/tanstack-start linked locally (for example workspace:*). This avoids Node 22 / Vercel trying to load raw adapter TypeScript from node_modules. ### 2. Create docs.config.tsx ### 3. Update vite.config.ts ### 4. Create src/lib/docs.server.ts ### 5. Create src/lib/docs.functions.ts ### 6. Wrap the app with RootProvider ### 7. Render docs routes ### 8. Import the theme CSS in src/styles/app.css ### 9. Add the docs API route That is the full manual setup. The built-in TanstackDocsPage renderer handles MDX page modules for you, so there is no extra docs-page.tsx or doc-modules.ts file to maintain. ### 1. Install packages ### 2. Create src/lib/docs.config.ts ### 3. Create src/lib/docs.server.ts ### 4. Create route files src/routes/docs/+layout.svelte: src/routes/docs/+layout.server.js: src/routes/docs/[...slug]/+page.svelte: src/routes/api/docs/+server.js (for search and AI): > Production note: The docs.server.ts file uses import.meta.glob to bundle your markdown files at build time. This is required for serverless deployments (Vercel, Netlify, etc.) where the filesystem is not available at runtime. ### 5. Import theme CSS in src/app.css Add your theme's CSS to your global stylesheet so docs styling applies. Use the import that matches the theme in your config (e.g. fumadocs below; for greentree use @farming-labs/svelte-theme/greentree/css). ### 6. Write your first doc Create Markdown files under docs/: Each page uses frontmatter for metadata: ### 1. Install packages ### 2. Create src/lib/docs.config.ts ### 3. Create src/lib/docs.server.ts ### 4. Create page routes Import your theme's CSS in each docs page (or in a global CSS file and import that). The example below uses @farming-labs/astro-theme/css (default); for other themes use e.g. @farming-labs/astro-theme/greentree/css. src/pages/docs/index.astro and src/pages/docs/[...slug].astro: ### 5. Create API route src/pages/api/docs.ts: ### 6. Astro config Enable SSR in astro.config.mjs: ### 7. Write your first doc Create Markdown files under docs/: Each page uses frontmatter for metadata: ### 1. Install packages ### 2. Create docs.config.ts ### 3. Configure nuxt.config.ts Add your theme's CSS via the css array so docs styling applies. Use the path that matches the theme in your config (e.g. fumadocs below; for greentree use @farming-labs/nuxt-theme/greentree/css). ### 4. Create server/api/docs.ts ### 5. Create pages/docs/[...slug].vue > Nuxt uses Nitro's server assets to load markdown files at runtime. The serverAssets config in nuxt.config.ts tells Nitro where your docs directory is. ### 6. Write your first doc Create Markdown files under docs/`: Each page uses frontmatter for metadata: --- ## API Reference URL: https://docs.farming-labs.dev/docs/reference Complete reference for all configuration options API Reference Complete reference for every option in docs.config.ts. All types are exported from @farming-labs/docs. defineDocs(config) The entry point for configuring your docs. Returns a typed config object. DocsConfig Top-level configuration object passed to defineDocs(). | Property | Type | Default | Description | | -------------- | ----------------------------------------------- | ---------------- | ------------------------------------------------------------------------------------ | | entry | string | required | URL path prefix for docs (e.g. "docs" → /docs) | | contentDir | string | same as entry | Path to content files. TanStack Start, SvelteKit, Astro, and Nuxt use it; Next.js uses app/{entry}/ | | staticExport | boolean | false | Set true for full static builds; hides search and AI (see Configuration) | | theme | DocsTheme | — | Theme preset from a factory (fumadocs(), darksharp(), pixelBorder(), etc.) | | github | string \| GithubConfig | — | GitHub repo config for "Edit on GitHub" links | | nav | DocsNav | — | Sidebar header title and URL | | themeToggle | boolean \| ThemeToggleConfig | true | Light/dark mode toggle | | breadcrumb | boolean \| BreadcrumbConfig | true | Breadcrumb navigation | | sidebar | boolean \| SidebarConfig | true | Sidebar visibility and customization | | icons | Record | — | Icon registry for frontmatter icon fields | | components | Record | — | Custom MDX component overrides, including built-ins like HoverLink | | onCopyClick | (data: CodeBlockCopyData) => void | — | Callback when the user clicks the copy button on a code block (runs in addition to copy) | | feedback | boolean \| FeedbackConfig | false | Human page feedback UI plus optional agent feedback endpoints | | pageActions | PageActionsConfig | — | "Copy Markdown" and "Open in LLM" buttons | | ai | AIConfig | — | RAG-powered AI chat | | search | boolean \| DocsSearchConfig | true | Built-in simple search, Typesense, Algolia, or a custom adapter | | mcp | boolean \| DocsMcpConfig | enabled | Built-in MCP server over stdio and /api/docs/mcp | | apiReference | boolean \| ApiReferenceConfig | false | Generated API reference pages from supported framework route conventions or a hosted OpenAPI JSON | | changelog | boolean \| ChangelogConfig | false | Generated changelog feed and entry pages from dated MDX entries | | ordering | "alphabetical" \| "numeric" \| OrderingItem[] | "alphabetical" | Sidebar page ordering strategy | | metadata | DocsMetadata | — | SEO metadata | | og | OGConfig | — | Open Graph image config | components is merged into the default MDX component map, so you can both add your own components and replace built-ins such as Callout, Tabs, or HoverLink. For built-in defaults like theme.ui.components.HoverLink, see Creating themes. For usage examples and a live demo, see Components. Ordering Controls how pages are sorted in the sidebar. Three strategies are available: "alphabetical" (default) Pages are sorted alphabetically by folder name. No extra configuration needed. "numeric" Pages are sorted by the order field in frontmatter. Lower numbers appear first. Pages without order are sorted alphabetically after ordered pages. Then in your page frontmatter: Slug-based (OrderingItem[]) Pass an array of { slug, children? } objects for explicit control over page order, including nested pages. OrderingItem | Property | Type | Description | | ---------- | ---------------- | --------------------------------------------- | | slug | string | Folder name at this level (not the full path) | | children | OrderingItem[] | Ordering for child pages within this folder | Pages not listed in the array appear alphabetically after the listed ones. DocsNav Sidebar header configuration. | Property | Type | Default | Description | | -------- | --------------------- | ------------ | --------------------------------------------------------- | | title | string \| ReactNode | "Docs" | Sidebar header title. React elements supported in Next.js and TanStack Start | | url | string | "/{entry}" | URL the title links to | DocsMcpConfig Built-in MCP server configuration for AI clients, IDE agents, and local MCP tooling. | Property | Type | Default | Description | | ---------- | --------------------- | ----------------- | ----------- | | enabled | boolean | true | Enable the MCP server. MCP is on by default; set false to opt out. | | route | string | "/api/docs/mcp" | Streamable HTTP route used by the MCP endpoint | | name | string | nav.title or @farming-labs/docs | MCP server name reported to clients | | version | string | "0.0.0" | Version string reported to clients | | tools | DocsMcpToolsConfig | all enabled | Fine-grained tool toggles | DocsMcpToolsConfig | Property | Type | Description | | --------------- | --------- | ----------- | | listPages | boolean | Expose the list_pages tool | | getNavigation | boolean | Expose the get_navigation tool | | searchDocs | boolean | Expose the search_docs tool | | readPage | boolean | Expose the read_page tool | Default MCP surface: - HTTP route: /api/docs/mcp - stdio command: pnpx @farming-labs/docs mcp - Built-in tools: listpages, getnavigation, searchdocs, readpage Framework notes: - Next.js: withDocs() auto-generates the default /api/docs/mcp route - TanStack Start / SvelteKit / Astro / Nuxt: add the matching framework route file and forward to the built-in MCP handler from the docs server helper - Custom routes: set mcp.route in docs.config and add the matching route file manually so the configured path and the actual endpoint stay aligned See MCP Server for the route snippets and examples. DocsSearchConfig Search is enabled by default. If you do not configure anything, the docs API uses the built-in simple adapter with section-based chunking. Built-in providers: - simple — zero-config docs search with section chunking - typesense — external Typesense collection with optional hybrid mode - algolia — external Algolia index - mcp — Streamable HTTP MCP endpoint that exposes a search_docs tool - custom — your own adapter implementation SimpleDocsSearchConfig | Property | Type | Default | Description | | ---------- | ----------------------- | ----------- | ----------- | | provider | "simple" | implied | Use the built-in search adapter | | enabled | boolean | true | Toggle search on or off | | maxResults | number | 10 | Maximum results to return | | chunking | DocsSearchChunkingConfig | { strategy: "section" } | Controls whether indexing happens per page or per section | TypesenseDocsSearchConfig | Property | Type | Default | Description | | ------------- | -------------------------- | ----------- | ----------- | | provider | "typesense" | — | Use Typesense as the search backend | | baseUrl | string | — | Typesense API base URL | | collection | string | — | Collection name used for docs documents | | apiKey | string | — | Search API key | | adminApiKey | string | — | Optional admin key used for on-demand indexing | | maxResults | number | 10 | Maximum results to return | | syncOnSearch | boolean | true when adminApiKey is present | Re-index docs on the first search request | | queryBy | string[] | built-in fields | Custom Typesense query_by fields | | mode | "keyword" \| "hybrid" | "keyword" | Keyword search only, or keyword + embeddings | | embeddings | DocsSearchEmbeddingsConfig | — | Semantic embeddings config for hybrid search | | chunking | DocsSearchChunkingConfig | { strategy: "section" } | Chunking strategy used before indexing/search | AlgoliaDocsSearchConfig | Property | Type | Default | Description | | -------------- | ----------------------- | ----------- | ----------- | | provider | "algolia" | — | Use Algolia as the search backend | | appId | string | — | Algolia application id | | indexName | string | — | Algolia index name | | searchApiKey | string | — | Search API key | | adminApiKey | string | — | Optional admin key used for on-demand indexing | | maxResults | number | 10 | Maximum results to return | | syncOnSearch | boolean | true when adminApiKey is present | Re-index docs on the first search request | | chunking | DocsSearchChunkingConfig | { strategy: "section" } | Chunking strategy used before indexing/search | McpDocsSearchConfig | Property | Type | Default | Description | | ---------- | ----------------------- | -------------- | ----------- | | provider | "mcp" | — | Use an MCP endpoint as the search backend | | endpoint | string | — | Streamable HTTP MCP endpoint | | headers | Record | — | Extra headers sent to initialize and tool calls | | toolName | string | "search_docs" | MCP tool name used for search | | protocolVersion | string | "2025-11-25" | MCP protocol version header | | maxResults | number | 10 | Maximum results to request | Relative endpoints are resolved against the current docs API request URL. That makes local setups like the Next example easy to test without hardcoding a host. CustomDocsSearchConfig | Property | Type | Description | | ---------- | ------------------------------ | ----------- | | provider | "custom" | Use your own adapter | | adapter | DocsSearchAdapter \| DocsSearchAdapterFactory | Search runtime implementation | | maxResults | number | Maximum results to return | | chunking | DocsSearchChunkingConfig | Chunking strategy used before your adapter runs | DocsSearchAdapter Custom adapters receive normalized source pages and chunked search documents so you can focus on retrieval and ranking instead of rebuilding the docs scan pipeline. DocsSearchChunkingConfig | Property | Type | Default | Description | | ---------- | ----------------------- | ----------- | ----------- | | strategy | "page" \| "section" | "section" | Chunk by whole page or split by headings | DocsSearchEmbeddingsConfig | Property | Type | Default | Description | | ---------- | ----------- | ------------------------- | ----------- | | provider | "ollama" | — | Embeddings provider for hybrid Typesense mode | | model | string | — | Embedding model id | | baseUrl | string | "http://127.0.0.1:11434" | Ollama base URL | Notes: - search: false disables search entirely - Search requires the docs API route; static export hides the UI because there is no server route - On Next.js, generated routes from withDocs() already forward docsConfig.search - MCP-backed search works with relative endpoints like /api/docs/mcp and absolute remote endpoints like https://docs.example.com/api/docs/mcp - If MCP-backed search points at the same relative MCP route, the built-in search_docs tool falls back to simple search internally to avoid recursive loops - On custom/manual Next routes, import createDocsAPI from @farming-labs/next/api and pass search: docsConfig.search @farming-labs/theme/api remains supported as a compatibility import path today, but prefer @farming-labs/next/api in Next.js apps. The theme-level path will be deprecated. ApiReferenceConfig Generates API reference pages from framework route conventions or a hosted OpenAPI JSON document. Use route scanning when the API lives in the same project. Use specUrl when the backend is deployed elsewhere and already exposes an openapi.json. apiReference is supported in Next.js, TanStack Start, SvelteKit, Astro, and Nuxt. apiReference in docs.config controls scanning, theming, routeRoot, and exclude. In Next.js, withDocs() also generates the route automatically. In TanStack Start, SvelteKit, Astro, and Nuxt, you must additionally add the framework route handler for /{path}. specUrl is the easiest option when your docs app does not contain the backend route files. Point it at a hosted openapi.json and keep the same themed API reference UI. | Property | Type | Default | Description | | ----------- | ---------- | ----------------- | ----------- | | enabled | boolean | true inside the object | Enable generated API reference pages | | path | string | "api-reference" | URL path where the generated API reference lives | | specUrl | string | — | Absolute URL to a hosted OpenAPI JSON document. When set, local route scanning is skipped | | routeRoot | string | "api" | Filesystem route root to scan. Bare values like "api" resolve inside app/ or src/app/; full values like "app/internal-api" and "src/app/v2/api" are supported | | exclude | string[] | [] | Route entries to omit from the generated reference. Accepts URL-style paths like "/api/hello" or route-root-relative values like "hello" / "hello/route.ts" | When specUrl is set, routeRoot and exclude are ignored and the API reference is rendered from the hosted spec instead. This does not remove the framework route requirement on non-Next adapters. TanStack Start, SvelteKit, Astro, and Nuxt still need their /{path} handler files so the docs app has a route that serves the generated API reference page. When output: "export" is used in Next.js, the generated API reference route is skipped automatically because it requires a server route handler. Scanned route conventions: - Next.js: app/api//route.ts and src/app/api//route.ts - TanStack Start: src/routes/api.*.ts and nested route files inside the configured route root - SvelteKit: src/routes/api//+server.ts or +server.js - Astro: src/pages/api//*.ts or .js - Nuxt: server/api//*.ts or .js Minimal route handlers for non-Next frameworks: Create a second src/routes/api-reference.$.ts file with the same handler and createFileRoute("/api-reference/$"). Create src/routes/api-reference/[...slug]/+server.ts with the same GET export. Create src/pages/api-reference/[...slug].ts with the same GET export. Create server/routes/api-reference/[...slug].ts with the same default export. ChangelogConfig Generates changelog listing and entry pages from dated MDX folders inside the docs content tree. The turn-key generated changelog pages are currently wired in Next.js when you use withDocs(). | Property | Type | Default | Description | | -------- | ---- | ------- | ----------- | | enabled | boolean | true inside the object | Enable generated changelog pages | | path | string | "changelog" | URL path where the changelog listing lives inside the docs layout | | contentDir | string | "changelog" | Source directory under the docs content root. Example: app/docs/changelog/2026-03-04/page.mdx | | title | string | "Changelog" | Listing page title | | description | string | — | Listing page description shown in the header and metadata | | search | boolean | true | Show the built-in changelog search field | | actionsComponent | ReactNode \| Component | — | Custom action content rendered in the changelog rail | With entry: "docs" and the config above, the public pages render at: - /docs/changelogs - /docs/changelogs/2026-03-04 When you use withDocs(), the route files are generated automatically. There is no separate changelog.generated.tsx file to maintain. ChangelogFrontmatter Optional frontmatter fields for each changelog entry: | Property | Type | Description | | -------- | ---- | ----------- | | title | string | Entry title shown in the feed and entry page | | description | string | Short summary shown in the feed and metadata | | image | string | Cover image path or URL | | authors | string \| string[] | Author name(s) shown in entry meta | | version | string | Compact version badge like v0.1.0 | | tags | string[] | Badge list shown in the feed and entry page | | pinned | boolean | Keep important entries pinned to the top of the listing | | draft | boolean | Hide the entry from generated routes and search indexes | GithubConfig GitHub repository configuration. Enables "Edit on GitHub" links in page footers. | Property | Type | Default | Description | | ----------- | -------- | ------------ | -------------------------------------------------------- | | url | string | required | Repository URL (e.g. "https://github.com/my-org/repo") | | branch | string | "main" | Branch name | | directory | string | — | Content subdirectory for monorepos | The "Edit on GitHub" link only appears when both url and directory are provided. onCopyClick and CodeBlockCopyData Optional callback on DocsConfig. Fired when the user clicks the copy button on a code block — in addition to copying to the clipboard. Use it for analytics, logging, or custom behavior. See Code block copy callback for setup (Next.js / TanStack Start vs SvelteKit / Astro / Nuxt). CodeBlockCopyData Argument passed to onCopyClick(data: CodeBlockCopyData). | Property | Type | Description | | ---------- | -------- | --------------------------------------------------------------------------- | | title | string \| undefined | Code block title (e.g. from fenced code meta), if present | | content | string | Raw code content (what was copied) | | url | string | Current page URL at the time of copy | | language | string \| undefined | Language/syntax hint (e.g. "tsx", "bash"), if present | feedback, FeedbackConfig, AgentFeedbackConfig, and feedback payloads Optional docs page feedback UI. When enabled, a built-in "Good / Bad" prompt is rendered at the end of the page and emits a feedback payload when the user clicks one of the buttons. FeedbackConfig | Property | Type | Default | Description | | --------------- | ---------------------------------------- | ----------------- | -------------------------------------------- | | enabled | boolean | true | Show or hide the feedback UI | | question | string | "How is this guide?" | Prompt shown above the buttons | | positiveLabel | string | "Good" | Label for the positive button | | negativeLabel | string | "Bad" | Label for the negative button | | onFeedback | (data: DocsFeedbackData) => void | — | Callback fired when the user clicks a button | | agent | boolean \| AgentFeedbackConfig | — | Agent feedback routes served through /api/docs | DocsFeedbackData | Property | Type | Description | | ------------- | -------------------------------------- | ----------------------------------------------------- | | value | "positive" \| "negative" | Which feedback button the user clicked | | title | string \| undefined | Current page title, when available | | description | string \| undefined | Current page description, when available | | url | string | Full current page URL | | pathname | string | Current pathname without origin | | path | string | Alias of pathname | | entry | string | Docs entry root, e.g. "docs" | | slug | string | Page slug relative to the docs entry | | locale | string \| undefined | Active locale when docs i18n is enabled | This docs site persists feedback through a local Next.js route at website/app/api/feedback/route.ts, backed by the DocsFeedback Prisma model in website/prisma/schema.prisma. See Page feedback for framework-specific notes. The built-in UI does not require a separate client bridge file. AgentFeedbackConfig Optional machine-facing feedback routes for agents, scripts, or evaluation flows. | Property | Type | Default | Description | | ------------- | --------------------------------------------------- | ---------------------------------- | ----------- | | enabled | boolean | true when agent is provided | Enable or disable the agent feedback endpoints | | route | string | "/api/docs/agent/feedback" | Public HTTP route for feedback submission | | schemaRoute | string | ${route}/schema | Public HTTP route for the machine-readable schema | | schema | Record | built-in payload schema | Schema used to validate the payload object | | onFeedback | (data: DocsAgentFeedbackData) => Promise \| void | — | Async callback fired after a valid submission | Notes: - feedback.agent does not enable the human footer UI by itself - the request body always uses { context?, payload } - in Next.js, withDocs() adds the public route rewrites automatically - the shared /api/docs handler is still the source of truth, so ?feedback=agent also works - agents should discover site identity, locale config, capability flags, search, active markdown routes and Accept: text/markdown support, llms.txt, skills, MCP, and feedback routes with GET /.well-known/agent.json; GET /.well-known/agent is the public fallback and GET /api/docs/agent/spec is the canonical framework route DocsAgentFeedbackContext | Property | Type | Description | | -------- | ----------------------- | ----------- | | page | string \| undefined | Docs page path such as "/docs/installation" | | url | string \| undefined | Full docs URL when available | | slug | string \| undefined | Docs slug relative to the entry root | | locale | string \| undefined | Active locale when docs i18n is enabled | | source | string \| undefined | Arbitrary source label such as "md-route", "mcp", or "api" | DocsAgentFeedbackData | Property | Type | Description | | --------- | ------------------------------------- | ----------- | | context | DocsAgentFeedbackContext \| undefined | Optional docs/page transport context | | payload | Record | Agent feedback payload validated by feedback.agent.schema | Quick checks: ThemeToggleConfig Light/dark mode toggle in the sidebar. | Property | Type | Default | Description | | --------- | ------------------------------------- | -------------- | ---------------------------------- | | enabled | boolean | true | Show or hide the toggle | | default | "light" \| "dark" \| "system" | "system" | Forced theme when enabled: false | | mode | "light-dark" \| "light-dark-system" | "light-dark" | Toggle behavior | BreadcrumbConfig Breadcrumb navigation above page content. | Property | Type | Default | Description | | ----------- | ----------- | ------- | ------------------------------------------ | | enabled | boolean | true | Show or hide breadcrumbs | | component | Component | — | Custom breadcrumb component (Next.js only) | SidebarConfig Sidebar visibility and customization. See Sidebar for the full guide including custom sidebar components. | Property | Type | Default | Description | | ------------- | ------------------------------------------------ | ------- | ------------------------------------------------------------------------------------------------------------ | | enabled | boolean | true | Show or hide the sidebar | | component | (props: SidebarComponentProps) => ReactNode | — | Custom sidebar render function. Receives { tree, collapsible, flat }. Supported in Next.js and TanStack Start — other frameworks use slots. | | footer | ReactNode | — | Content rendered below nav items | | banner | ReactNode | — | Content rendered above nav items | | collapsible | boolean | true | Whether sidebar is collapsible on desktop | | flat | boolean | false | Render all items flat (Mintlify-style, no collapsible groups) | SidebarComponentProps Props passed to a custom sidebar component: | Property | Type | Description | | ------------- | ------------- | ------------------------------------------------ | | tree | SidebarTree | Full page tree with all parent-child relationships | | collapsible | boolean | Whether folders are collapsible | | flat | boolean | Whether folders are rendered flat | SidebarTree | Property | Type | Description | | ---------- | ---------------- | ------------------------ | | name | string | Root name (e.g. "Docs") | | children | SidebarNode[] | Top-level sidebar items | SidebarNode A union of SidebarPageNode | SidebarFolderNode. SidebarPageNode | Property | Type | Description | | -------- | --------- | ------------------- | | type | "page" | Node type | | name | string | Display name | | url | string | Page URL | | icon | unknown | Icon from registry | SidebarFolderNode | Property | Type | Description | | ------------- | ------------------ | ----------------------------- | | type | "folder" | Node type | | name | string | Display name | | icon | unknown | Icon from registry | | index | SidebarPageNode | Folder's landing page | | children | SidebarNode[] | Child pages and sub-folders | | collapsible | boolean | Whether this folder collapses | | defaultOpen | boolean | Whether it starts open | DocsTheme Theme configuration returned by theme factories. | Property | Type | Description | | -------- | ---------- | --------------------------------------------------- | | name | string | Theme identifier (e.g. "fumadocs", "darksharp") | | ui | UIConfig | Visual configuration — colors, typography, layout | Created via theme factories: UIConfig Fine-grained visual configuration passed to theme factories. | Property | Type | Description | | ------------ | -------------------- | ----------------------------------------------- | | colors | ColorsConfig | Theme color tokens | | typography | TypographyConfig | Font families, heading styles | | radius | string | Global border-radius (e.g. "0px", "0.5rem") | | layout | LayoutConfig | Content width, sidebar width, TOC settings | | codeBlock | CodeBlockConfig | Syntax highlighting and line numbers | | sidebar | SidebarStyleConfig | Sidebar visual style | | card | CardConfig | Card component styling | ColorsConfig All color tokens. Accepts any CSS color value (hex, rgb, oklch, hsl). | Token | Description | | --------------------- | ----------------------------------- | | primary | Primary brand color | | primaryForeground | Text color on primary backgrounds | | background | Page background | | foreground | Default text color | | muted | Muted background (e.g. code blocks) | | mutedForeground | Text on muted backgrounds | | border | Default border color | | card | Card background | | cardForeground | Card text color | | accent | Accent color | | accentForeground | Text on accent backgrounds | | secondary | Secondary color | | secondaryForeground | Text on secondary backgrounds | | popover | Popover/dropdown background | | popoverForeground | Popover text color | | ring | Focus ring color | LayoutConfig | Property | Type | Default | Description | | --------------- | --------- | ------- | ------------------------------- | | contentWidth | number | — | Max width of content area in px | | sidebarWidth | number | — | Sidebar width in px | | tocWidth | number | — | Table of contents width in px | | toc.enabled | boolean | true | Show table of contents | | toc.depth | number | 3 | Max heading depth for TOC | | header.height | number | — | Header height in px | | header.sticky | boolean | true | Sticky header | CodeBlockConfig | Property | Type | Default | Description | | ----------------- | --------- | ------- | -------------------------- | | showLineNumbers | boolean | false | Show line numbers | | showCopyButton | boolean | true | Show copy button | | theme | string | — | Shiki theme for light mode | | darkTheme | string | — | Shiki theme for dark mode | SidebarStyleConfig | Property | Type | Default | Description | | ------------- | --------------------------------------- | ----------- | ------------------------- | | style | "default" \| "bordered" \| "floating" | "default" | Sidebar visual style | | background | string | — | Background color override | | borderColor | string | — | Border color override | TypographyConfig FontStyle Used for each heading level and body text. | Property | Type | Description | | --------------- | ------------------ | ------------------------------------------------------------------ | | size | string | CSS font-size (e.g. "2.25rem", "clamp(1.8rem, 3vw, 2.5rem)") | | weight | string \| number | CSS font-weight (e.g. 700, "bold") | | lineHeight | string | CSS line-height (e.g. "1.2", "28px") | | letterSpacing | string | CSS letter-spacing (e.g. "-0.02em") | AIConfig RAG-powered AI chat. See Ask AI for usage guide. | Property | Type | Default | Description | | -------------------- | ---------------------------------------------------- | ----------------------------- | ------------------------------------------------------- | | enabled | boolean | false | Enable AI chat | | mode | "search" \| "floating" | "search" | UI mode: integrated in search or floating widget | | position | "bottom-right" \| "bottom-left" \| "bottom-center" | "bottom-right" | Floating button position (only when mode: "floating") | | floatingStyle | "panel" \| "modal" \| "popover" \| "full-modal" | "panel" | Floating chat visual style | | triggerComponent | Component | — | Custom floating button component | | model | string | "gpt-4o-mini" | LLM model name (OpenAI-compatible) | | systemPrompt | string | auto-generated | Custom system prompt | | baseUrl | string | "https://api.openai.com/v1" | OpenAI-compatible API base URL | | apiKey | string | process.env.OPENAIAPIKEY | API key for the LLM provider | | maxResults | number | 5 | Number of doc pages to include as RAG context | | suggestedQuestions | string[] | — | Pre-filled questions shown when chat is empty | | aiLabel | string | "AI" | Display name for the AI assistant | | packageName | string | — | Package name the AI uses in import examples | | docsUrl | string | — | Base URL the AI uses for links | | loader | string | "shimmer-dots" | Loading indicator variant ("shimmer-dots", "circular", "dots", "typing", "wave", "bars", "pulse", "pulse-dot", "terminal", "text-blink", "text-shimmer", "loading-dots") | | loadingComponent | (props: { name: string }) => ReactNode | — | Custom loading component (overrides loader, Next.js only) | Floating styles | Style | Description | | -------------- | ----------------------------------------------------------------- | | "panel" | Tall panel sliding up from the button. No backdrop. | | "modal" | Centered modal with backdrop overlay, like Cmd+K search. | | "popover" | Compact popover near the button. | | "full-modal" | Full-screen immersive overlay with pills for suggested questions. | PageActionsConfig Action buttons shown on each docs page. See Page Actions for usage guide. | Property | Type | Default | Description | | -------------- | -------------------------------- | --------------- | --------------------------- | | position | "above-title" \| "below-title" | "below-title" | Where to render the buttons | | alignment | "left" \| "right" | "left" | Horizontal alignment of the action row | | copyMarkdown | boolean \| CopyMarkdownConfig | false | "Copy Markdown" button | | openDocs | boolean \| OpenDocsConfig | false | "Open in LLM" dropdown | CopyMarkdownConfig | Property | Type | Default | Description | | --------- | --------- | ------- | --------------- | | enabled | boolean | false | Show the button | OpenDocsConfig | Property | Type | Default | Description | | ----------- | -------------------- | ----------------- | ------------------ | | enabled | boolean | false | Show the dropdown | | providers | OpenDocsProvider[] | ChatGPT, Claude | LLM/tool providers | OpenDocsProvider | Property | Type | Description | | ------------- | ----------- | --------------------------------------------------------------------------------- | | name | string | Display name (e.g. "ChatGPT", "Claude") | | icon | ReactNode | Icon rendered next to the name | | urlTemplate | string | URL template. {url} is replaced with the page URL, {mdxUrl} with the raw MDX source URL, and {githubUrl} with the GitHub edit URL when github is configured. Use {url}.md when you want the public machine-readable page route. | DocsMetadata SEO metadata configuration. | Property | Type | Default | Description | | --------------- | ------------------------------------ | ------- | ---------------------------------------------------------- | | titleTemplate | string | — | Page title template. %s is replaced with the page title. | | description | string | — | Default meta description | | twitterCard | "summary" \| "summarylargeimage" | — | Twitter card type | OGConfig Open Graph image configuration. When endpoint is set, each docs page gets a dynamic OG image: the framework passes the page’s title and description (from frontmatter) to the endpoint, and the layout injects the resulting image URL into og:image and twitter:image meta tags. For a full guide (how dynamic OG works, what context is passed, and how the docs website implements it), see OG Images. | Property | Type | Default | Description | | -------------- | ----------------------- | ------- | ----------------------------------- | | enabled | boolean | false | Enable OG image generation | | type | "static" \| "dynamic" | — | Static images or dynamic generation | | endpoint | string | — | API endpoint for dynamic OG images | | defaultImage | string | — | Fallback OG image URL | Example (Next.js): In docs.config.ts, set og.endpoint to your OG route path. Then implement app/api/og/route.tsx (or route.ts) that accepts title and description query params and returns a 1200×630 image (e.g. using ImageResponse from next/og). Testing your OG image 1. Start the dev server for the app that serves the OG route (e.g. cd website && pnpm dev or cd examples/next && pnpm dev). 2. Open the OG URL in the browser, e.g. http://localhost:3000/api/og?title=Test&description=My+description (use your app’s port if different). 3. After changing the OG route code, refresh the page. If the image doesn’t update, do a hard refresh (e.g. Cmd+Shift+R on macOS, Ctrl+Shift+R on Windows) or open the URL in a private/incognito window. You can also add a cache-busting query param (e.g. &=1, &=2) to force a new request. Page Frontmatter Each docs page (.mdx / .md) supports these frontmatter fields: | Property | Type | Description | | ------------- | ----------- | --------------------------------------------------------------------------------- | | title | string | Required. Page title, shown in sidebar and page heading | | description | string | Page description, used for meta tags and search | | icon | string | Icon key from the icons registry. Shows in sidebar. | | order | number | Sort order in the sidebar (only when ordering: "numeric"). Lower numbers first. | | tags | string[] | Tags for categorization | | ogImage | string | Shorthand for a single OG image path. Ignored if openGraph is set. | | openGraph | PageOpenGraph | Full Open Graph object (e.g. images: [{ url, width?, height? }]). When set, replaces generated OG from the config (e.g. dynamic endpoint). | | twitter | PageTwitter | Full Twitter card object (e.g. card, images). When set, replaces generated twitter metadata. | | hidden | boolean | If true, hide the page from the sidebar (page remains reachable by URL). Optional; behavior may vary by adapter. | Static OG example — use openGraph and twitter in frontmatter to serve a static image instead of the dynamic OG endpoint: Minimal example (shorthand ogImage or generated OG): Data types Frontmatter is YAML between the --- fences. Supported value types: | Type | Example in frontmatter | Use in framework fields | | --------- | ------------------------------ | -------------------------- | | string | title: "Hello" or title: Hello | title, description, icon, ogImage | | number | order: 1 | order | | boolean | hidden: true | Optional (e.g. hide from sidebar) | | string[]| tags: [a, b] or tags: ["a", "b"] | tags | You can use other YAML types (e.g. nested objects) for custom keys; only the fields in the table above are used by the framework for sidebar, SEO, and OG. How frontmatter is passed - Next.js — The MDX pipeline uses remark-frontmatter and remark-mdx-frontmatter with name: "metadata". The parsed frontmatter is exposed as the metadata export from the page. The layout and generateMetadata receive it when resolving the page (e.g. via getPage(params)), and it is used for createPageMetadata, sidebar tree (title, icon, order), and description maps. - TanStack Start / SvelteKit / Astro / Nuxt — The docs loader (e.g. import.meta.glob or content layer) reads each .md/.mdx file, parses frontmatter, and builds the nav tree and page data. Frontmatter is passed to the layout as page data (e.g. page.data or the props your content component receives). The same fields (title, description, icon, order, tags, ogImage) are used for sidebar, meta tags, and search. So in all frameworks, the same frontmatter fields drive titles, descriptions, icons, ordering, and OG; only the mechanism (MDX metadata vs. content loader page.data) differs. createTheme(options) Create a custom theme from scratch. See Creating Themes. extendTheme(base, overrides) Extend an existing theme with overrides. TanStack Start Server API createDocsServer(config) Creates the TanStack Start docs server adapter. Parameters: | Property | Type | Description | | --------- | ------------ | ----------- | | config | DocsConfig | The config object from defineDocs() | | rootDir | string | Optional project root used to resolve contentDir. Defaults to process.cwd() | Returns: | Property | Type | Description | | -------- | ---------------------------------------------------------- | ----------- | | load | ({ pathname, locale? }) => Promise | Loads docs page data and the sidebar tree for a pathname | | GET | ({ request }) => Response | Search / markdown / llms.txt endpoint handler | | POST | ({ request }) => Promise | AI chat endpoint handler | TanStack Start Components | Component | Import | Description | | ------------------ | ------------------------------------ | ----------- | | TanstackDocsPage | @farming-labs/tanstack-start/react | Built-in page renderer that resolves the compiled MDX module and wraps it in the shared docs layout | | RootProvider | @farming-labs/theme/tanstack | Root provider for theme state, search, and AI UI | TanStack Start also uses the docsMdx() Vite plugin from @farming-labs/tanstack-start/vite so .mdx files compile without extra local module-loader glue. SvelteKit Server API createDocsServer(config) Creates all server-side functions for a SvelteKit docs site. Parameters: | Property | Type | Description | | ------------------- | ------------------------ | --------------------------------------------------------------------------- | | config | DocsConfig | The config object from defineDocs() | | _preloadedContent | Record | Pre-loaded markdown files from import.meta.glob. Required for serverless. | Returns: | Property | Type | Description | | -------- | ------------------------------ | ------------------------------------------------- | | load | (event) => Promise | Layout load function (use in +layout.server.js) | | GET | (event) => Response | Search endpoint handler | | POST | (event) => Promise | AI chat endpoint handler | SvelteKit Components Imported from @farming-labs/svelte-theme: | Component | Props | Description | | ------------- | ------------------------------------------------------------------------------------------------------ | ---------------------------------------------- | | DocsLayout | tree, config, title?, titleUrl? | Main layout with sidebar, nav, search, AI | | DocsContent | data, config | Page content with TOC, breadcrumb, footer nav | | DocsPage | entry, tocEnabled, breadcrumbEnabled, previousPage, nextPage, editOnGithub, lastModified | Low-level page wrapper (used by DocsContent) | Astro Server API createDocsServer(config) Same as SvelteKit — returns { load, GET, POST }. Parameters: | Property | Type | Description | | ------------------- | ------------------------ | --------------------------------------------------------------------------- | | config | DocsConfig | The config object from defineDocs() | | _preloadedContent | Record | Pre-loaded markdown files from import.meta.glob. Required for serverless. | Returns: | Property | Type | Description | | -------- | ----------------------------------------- | ----------------------------------------------------- | | load | (pathname: string) => Promise | Takes a URL pathname string, returns page data | | GET | ({ request }) => Response | Search endpoint handler for GET /api/docs?query=... | | POST | ({ request }) => Promise | AI chat endpoint handler with SSE streaming | Astro Components | Component | Import | Description | | -------------- | ------------------------------------------------------------- | -------------------------------------------------- | | DocsLayout | @farming-labs/astro-theme/src/components/DocsLayout.astro | Main layout with sidebar, search, and theme toggle | | DocsContent | @farming-labs/astro-theme/src/components/DocsContent.astro | Page content with metadata | | DocsPage | @farming-labs/astro-theme/src/components/DocsPage.astro | Page structure (TOC, breadcrumb, nav) | | ThemeToggle | @farming-labs/astro-theme/src/components/ThemeToggle.astro | Light/dark toggle | | SearchDialog | @farming-labs/astro-theme/src/components/SearchDialog.astro | Search + AI dialog | CSS Imports Same pattern as SvelteKit: | Import | Description | | -------------------------------------------- | ------------------------ | | @farming-labs/astro-theme/css | Default (fumadocs) theme | | @farming-labs/astro-theme/pixel-border/css | Pixel border theme | | @farming-labs/astro-theme/darksharp/css | Darksharp theme | Theme Factories | Factory | Import | | --------------- | ---------------------------------------- | | fumadocs() | @farming-labs/astro-theme | | pixelBorder() | @farming-labs/astro-theme/pixel-border | | darksharp() | @farming-labs/astro-theme/darksharp | Nuxt Server API defineDocsHandler(config, useStorage) Creates a single Nitro event handler that serves docs loading, search, and AI chat for Nuxt. Parameters: | Property | Type | Description | | ------------ | ------------ | -------------------------------------------------------- | | config | DocsConfig | The config object from defineDocs() | | useStorage | Function | Nitro's useStorage utility for accessing server assets | Returns: A Nitro event handler that responds to GET (page loading + search) and POST (AI chat) requests. Nuxt uses Nitro's serverAssets to load markdown files. Configure nitro.serverAssets in nuxt.config.ts to point to your docs directory. Nuxt Components Imported from @farming-labs/nuxt-theme: | Component | Props | Description | | ------------- | ------------------------------------- | --------------------------------------------- | | DocsLayout | tree, config, triggerComponent? | Main layout with sidebar, nav, search, AI | | DocsContent | data, config | Page content with TOC, breadcrumb, footer nav | CSS Imports | Import | Description | | --------------------------------------- | ------------------------ | | @farming-labs/nuxt-theme/fumadocs/css | Default (fumadocs) theme | Theme Factories | Factory | Import | | ------------ | ----------------------------------- | | fumadocs() | @farming-labs/nuxt-theme/fumadocs | Next.js API RootProvider Client-side provider that wraps your entire app. Enables search, theme switching (light/dark), and AI chat. Must be placed in your root app/layout.tsx. | Prop | Type | Default | Description | | ---------- | --------------------- | ----------------------------------- | --------------------------------------------------------------- | | search | SearchProviderProps | { options: { api: "/api/docs" } } | Search configuration. API defaults to the unified docs handler. | | theme | ThemeProviderProps | — | Theme provider options (passed to next-themes) | | children | ReactNode | required | Your app content | suppressHydrationWarning on prevents React warnings caused by the theme class being injected before hydration. withDocs(nextConfig) Wraps your Next.js config with docs framework support. Handles MDX, routing, search index generation, and auto-generates mdx-components.tsx and app/docs/layout.tsx if missing. createDocsLayout(config) Creates the docs layout component from your config. Auto-generated by withDocs()` — you typically don't need to create this manually. --- ## Themes URL: https://docs.farming-labs.dev/docs/themes Built-in themes and how to create your own Themes @farming-labs/docs ships with ten built-in theme entrypoints. Each is a preset factory function that you pass to defineDocs(). hardline stays as the original hard-edge preset, concrete is the louder brutalist variant, and command-grid is the mono-first paper-grid preset inspired by the better-cmdk landing page. Want to build a custom theme or publish one as a package? See Creating your own theme for the full guide. Built-in Themes | Theme | Import | Description | | ----------------------------------------- | ---------------------------------- | --------------------------------------- | | Default | @farming-labs/theme | Neutral colors, standard radius | | Colorful | @farming-labs/theme/colorful | Warm amber accent, Inter typography | | Darksharp | @farming-labs/theme/darksharp | All-black, sharp corners | | Pixel Border | @farming-labs/theme/pixel-border | Inspired by better-auth.com | | Shiny | @farming-labs/theme/shiny | Clerk-inspired, purple accents | | DarkBold | @farming-labs/theme/darkbold | Pure monochrome, Geist typography | | GreenTree | @farming-labs/theme/greentree | Mintlify-inspired, emerald green accent | | Concrete | @farming-labs/theme/concrete | Brutalist poster-style hard-edge theme | | Command Grid | @farming-labs/theme/command-grid | Mono-first paper-grid preset inspired by better-cmdk | | Hardline | @farming-labs/theme/hardline | Original hard-edge theme with strong borders | Using a Theme Don't forget the matching CSS import: Overriding Theme Defaults Every theme accepts an overrides object: Creating a Custom Theme For a comprehensive guide including publishing your theme as a package, see Creating Your Own Theme. Use createTheme() to build your own: Extending a Theme Use extendTheme() to modify an existing preset: --- ## Colorful URL: https://docs.farming-labs.dev/docs/themes/colorful Warm amber accent with Inter typography and a clean layout Colorful Theme A warm, vibrant theme with an amber primary accent, Inter typography, and directional TOC styling. Usage Defaults | Property | Value | | ------------- | --------------------------- | | Primary | hsl(40, 96%, 40%) (Amber) | | Background | #ffffff | | Border | #e5e7eb | | Border radius | Standard | | Content width | 768px | | Sidebar width | 260px | | TOC style | Directional | Customizing --- ## Command Grid URL: https://docs.farming-labs.dev/docs/themes/command-grid Mono-first paper-grid theme inspired by the better-cmdk landing page Command Grid Theme A mono-first preset with a paper-grid light mode, lead-toned dark mode, sharper borders, and calmer surfaces inspired by the better-cmdk landing page. Usage Defaults | Property | Value | | ------------- | ----------------------------------- | | Primary | #141414 | | Background | #f8f6ed (light), #121212 (dark) | | Border radius | 0px | | Content width | 900px | | Sidebar width | 304px | Style Notes - Mono-first typography across the sidebar, TOC, and command surfaces - Paper-grid background in light mode with a quieter dark-mode grid - Sharper tables, callouts, search UI, and Ask AI surfaces - Better-cmdk-inspired light/dark palette without the heavier concrete shadow treatment Customizing --- ## Concrete URL: https://docs.farming-labs.dev/docs/themes/concrete Brutalist poster-style theme with offset shadows and square corners Concrete Theme A louder brutalist-style preset with poster typography, offset shadows, square corners, and bold contrast. Usage Defaults | Property | Value | | ------------- | ----------------------------------- | | Primary | #ff5b31 | | Background | #f6ead9 (light), #12100f (dark) | | Border radius | 0px | | Content width | 896px | | Sidebar width | 316px | Customizing --- ## Creating Your Own Theme URL: https://docs.farming-labs.dev/docs/themes/creating-themes Step-by-step guide to building, publishing, and sharing custom themes Creating Your Own Theme Everything starts with createTheme(). No CSS required — just a config object. Quick Start That's it. Users can override any of your defaults: Config Options Reference Here's everything you can configure in createTheme(). name Unique identifier for your theme. Used for debugging and CSS scoping. ui.colors Color tokens mapped to --color-fd-* CSS variables at runtime. Any valid CSS color value works (hex, rgb, hsl, oklch). You only need to set the ones you want to change — the rest are inherited from the base preset. ui.typography Font families, heading sizes, weights, line heights, and letter spacing. Each heading/text style accepts these properties: | Property | Type | Example | | --------------- | ------------------ | --------------------- | | size | string | "2.25rem", "36px" | | weight | number \| string | 700, "bold" | | lineHeight | string | "1.2", "28px" | | letterSpacing | string | "-0.02em" | Tip: Use CSS variables for fonts so users can load them with next/font: ui.radius Global border radius. Maps to CSS --radius. ui.layout Content area dimensions and structural options. ui.codeBlock Code block rendering options. ui.sidebar Sidebar visual style. | Style | Description | | ------------ | ------------------------------------------------------- | | "default" | Standard fumadocs sidebar | | "bordered" | Visible bordered sections (inspired by better-auth.com) | | "floating" | Floating card sidebar with subtle shadow | ui.card Card styling. ui.components Default props for built-in MDX components. ui.components changes the default props for built-in MDX components. For example, HoverLink can keep the built-in popover UI while changing its CTA label, indicator, or alignment across your docs. If you want to replace the component completely, use components.HoverLink in docs.config.tsx instead. Full Example Here's a complete theme with every option: Extending an Existing Theme Don't want to start from scratch? Use extendTheme() to build on top of a built-in preset: Works with any built-in theme: extendTheme() returns a DocsTheme directly (not a factory). Use createTheme() when building a reusable preset. Cherry-Picking from Built-in Themes Each built-in theme exports its defaults object: Publishing as an npm Package Users install and use: Advanced: CSS Customization The config options above cover most use cases. But if you need pixel-level control over specific components — like how the sidebar active state looks, code block borders, table styling, or callout appearance — you can write a CSS file alongside your theme. How it Works Every theme can ship a CSS file that sits on top of the config. The CSS file: 1. Imports a color preset (which includes fumadocs-ui core styles) 2. Overrides CSS variables for light (:root) and dark (.dark) modes 3. Targets specific component selectors to customize their appearance Starting Point Two presets are available: | Preset | Import | Best for | | ------- | ------------------------------------- | ------------------ | | Neutral | @farming-labs/theme/presets/neutral | Light-first themes | | Black | @farming-labs/theme/presets/black | Dark-first themes | Component CSS Selectors Here are all the selectors you can target to style individual components: Sidebar Code Blocks Inline Code Tables Callouts Tabs Prev/Next Navigation Cards Search Dialog Breadcrumb Page Action Buttons Scrollbar Selection & Misc Tips - Inspect in DevTools — the fastest way to find the right selector is to right-click an element, inspect it, and check its classes/attributes. - Use !important sparingly — fumadocs uses Tailwind utilities, so some overrides need !important. Check the built-in theme CSS files for examples. - Scope with .dark — use .dark for dark mode styles. Fumadocs uses class-based dark mode. - Reference CSS variables — always use var(--color-fd-*) in your styles so they respond to user color overrides. - Look at built-in themes — the pixel-border and darksharp CSS files are excellent references showing every component selector in use. --- ## DarkBold URL: https://docs.farming-labs.dev/docs/themes/darkbold Pure monochrome design with Geist typography and clean minimalism DarkBold Theme A pure monochrome design inspired by Vercel — featuring Geist typography, tight letter-spacing, and clean minimalism. Usage Defaults | Property | Value | | ------------- | ----------------- | | Primary | #000 (Black) | | Background | #fff | | Border | #eaeaea | | Border radius | Standard | | Content width | 768px | | Sidebar width | 260px | | Font | Geist, Geist Mono | Typography DarkBold uses the Geist font family with tighter letter-spacing for headings: - H1: 2.5rem, weight 600, letter-spacing -0.06em - H2: 2rem, weight 600, letter-spacing -0.04em - H3: 1.5rem, weight 600, letter-spacing -0.02em Customizing --- ## Darksharp URL: https://docs.farming-labs.dev/docs/themes/darksharp All-black theme with sharp corners Darksharp Theme An all-black theme with rounded-none styling — clean, minimal, and sharp. Usage Defaults | Property | Value | | ------------- | ---------------------- | | Primary | Near-white | | Background | #000000 (pure black) | | Border radius | 0px (no rounding) | | Content width | 860px | | Sidebar width | 286px | Customizing --- ## Default URL: https://docs.farming-labs.dev/docs/themes/default Clean, neutral palette with standard border radius Default Theme The default Fumadocs theme with a neutral color palette and standard border radii. Usage Defaults | Property | Value | | ------------- | ------------------ | | Primary | #6366f1 (Indigo) | | Background | #ffffff | | Border radius | Standard | | Content width | 768px | | Sidebar width | 280px | Customizing --- ## GreenTree URL: https://docs.farming-labs.dev/docs/themes/greentree Mintlify-inspired theme with emerald green accent and Inter typography GreenTree Theme Inspired by Mintlify — a clean, modern design with an emerald green accent, Inter typography, and a compact sidebar. Usage Defaults | Property | Value | | ------------- | ------------------- | | Primary | #0D9373 (Emerald) | | Background | #fff | | Muted | #505351 | | Border | #DFE1E0 | | Border radius | Standard | | Content width | 768px | | Sidebar width | 240px | | Header height | 56px | Customizing --- ## Hardline URL: https://docs.farming-labs.dev/docs/themes/hardline Hard-edge theme with square corners and strong borders Hardline Theme hardline() is the original hard-edge preset: square corners, bold borders, and sharp high-contrast surfaces. If you want something more brutalist and poster-like, use Concrete instead. Usage Defaults | Property | Value | | ------------- | ----------------------------------- | | Primary | #ffd335 | | Background | #f2efe8 (light), #0f0f10 (dark) | | Border radius | 0px | | Content width | 860px | | Sidebar width | 300px | --- ## Pixel Border URL: https://docs.farming-labs.dev/docs/themes/pixel-border Inspired by better-auth.com — refined dark UI with visible borders Pixel Border Theme Inspired by better-auth.com — a clean dark UI with visible borders, pixel-perfect spacing, and a refined sidebar. The active sidebar indicator and text use your primary color. Usage Defaults | Property | Value | | ------------- | ----------------------------------------- | | Primary | Near-white (oklch(0.985 0.001 106.423)) | | Background | hsl(0 0% 2%) | | Border | hsl(0 0% 15%) | | Border radius | 0px | | Content width | 860px | | Sidebar width | 286px | Sidebar Features - Full-width border separators between top-level items - Collapsible folder groups with expand/collapse arrows - Active state: primary-colored text + left vertical indicator bar - Nested children are indented with smaller text - Folder parents use slightly muted text when collapsed Customizing The primary color flows to: - Active sidebar item text - Active sidebar indicator bar (::before) - Active TOC item - Links and other primary-colored elements --- ## Shiny URL: https://docs.farming-labs.dev/docs/themes/shiny Clerk-inspired theme with purple accents and a polished light/dark design Shiny Theme Inspired by Clerk — a clean, polished UI with purple accents and a professional light/dark design. Usage Defaults | Property | Value | | ------------- | ------------------------------ | | Primary | hsl(256, 100%, 64%) (Purple) | | Background | #f7f7f8 | | Border | #e5e5ea | | Border radius | Standard | | Content width | 768px | | Sidebar width | 280px | | Header height | 64px | Customizing --- ## Token Efficiency URL: https://docs.farming-labs.dev/docs/token-efficiency How @farming-labs/docs keeps your project lean for AI-assisted development — without sacrificing flexibility Token Efficiency AI tools like Cursor, Copilot, and Claude read your project files to help you write code. Every file they read costs tokens — the units that determine response speed, API cost, and how much of your project fits in a single conversation. @farming-labs/docs is built to keep the framework footprint small so more of your token budget goes to your actual documentation content — while still giving you the full flexibility of a modern docs framework. One Config, Full Control The core idea: everything about your docs site — theme, colors, typography, sidebar, AI chat, metadata — lives in a single docs.config.ts file. An AI agent reads this one file and immediately understands your entire docs setup — what theme you're using, where content lives, how AI chat is configured, how pages are titled. There's a provider wrapper (RootProvider) and a docs layout file, but they're minimal one-liners that the CLI generates for you. The real configuration surface is this single file. This matters because every additional config file, layout wrapper, or routing file is something an AI has to read, reason about, and avoid accidentally breaking. Fewer framework files means more room in the context window for what actually matters: your documentation content. The CLI Does the Heavy Lifting You don't have to set any of this up manually. The CLI scaffolds everything in seconds — for both new and existing projects. Starting from scratch Use --template to bootstrap a complete project with your framework and theme of choice: This creates a new my-docs/ folder with a fully working docs site — config, routes, CSS, sample pages, dependencies installed, dev server running. Pick from next, tanstack-start, nuxt, sveltekit, or astro. Want a specific theme? Add --theme: That's it — a beautiful themed docs site in one command. Adding to an existing project Already have a Next.js, TanStack Start, SvelteKit, Astro, or Nuxt project? Just run init inside it: The CLI auto-detects your framework from package.json, asks you to pick a theme, generates the config and minimal routing files, installs dependencies, and starts the dev server. Your existing code is untouched — it only adds what's needed for docs. See the CLI reference for all flags and options. Why This Matters for AI More context for your content AI models have limited context windows. Framework boilerplate eats into that budget. With @farming-labs/docs, the framework surface is roughly ~15 lines of config instead of hundreds of lines spread across many files. That leaves more room for your actual documentation pages when an AI is answering questions or making changes. Fewer files to reason about When an AI agent needs to modify your docs setup — change a theme, enable AI chat, adjust the sidebar — it reads your docs.config.ts and makes the change in one place. No hunting through layout files, provider trees, slug handlers, and CSS imports to figure out how everything connects. Less risk of breaking things A declarative config file is hard to break. An AI can add ai: { enabled: true } or change theme: darksharp() without worrying about import paths, component hierarchies, or routing logic. The framework handles all of that internally. Built-in AI Features Beyond being token-efficient to work with, @farming-labs/docs includes features designed for AI consumption: llms.txt Your docs are automatically served in LLM-optimized format — no extra routes needed: See the llms.txt docs for details. AI Chat with RAG Built-in AI chat with retrieval-augmented generation. A simple setup is just two lines: Need multiple models from different providers? Use the providers map and a model object with a selectable dropdown: Users get a model selector dropdown in the chat interface. The backend automatically routes each request to the correct provider with the right credentials. No separate API routes, no vector databases, no embedding pipelines — search indexing, context retrieval, and streaming responses are all handled internally. Works with any OpenAI-compatible provider: OpenAI, Groq, Together, Fireworks, OpenRouter, Ollama, or any vLLM deployment. See the AI Chat docs for the full configuration reference. Summary @farming-labs/docs keeps the framework lean — one config file, minimal routing, CLI-generated scaffolding — so you and your AI tools spend less time on plumbing and more time on content. And it does this without sacrificing flexibility: themes, multi-provider AI chat, search, custom components, and multi-framework support are all still there, just configured from one place. ---