Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Authoritative guide for the Fumadocs documentation framework — Core (headless), MDX (content loader), and UI (Tailwind v4 layouts and components). Use when scaffolding a Fumadocs project, editing source.config.ts, wiring loader() and meta.json conventions, picking a search adapter, composing DocsLayout/Notebook/Flux/Home layouts, customising the MDX component map, integrating OpenAPI specs, generating OG images, exporting PDF/EPUB/RSS, or configuring i18n across Next.js, React Router, TanStack S
.claude/skills/compozy-fumadocs/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 95% | 0% |
| case-03 | ✗→✓ | ▲ Improved | 17% | 0% |
| case-07 | ✗→✓ | ▲ Improved | 3% | 0% |
| case-09 | ✗→✓ | ▲ Improved | 231% | 0% |
| case-11 | ✗→✓ | ▲ Improved | 65% | 0% |
Fumadocs is a docs framework for React: it sits inside Next.js / React Router / TanStack Start / Waku and combines three independently usable packages.
| Package | Role | Skip when | | --- | --- | --- | | fumadocs-core | Headless engine: loader(), page tree, MDX plugins, search adapters, i18n primitives | never (always required) | | fumadocs-mdx | Content source: compiles content/**/*.mdx + meta.json into typed .source/ collections | replaced by @fumadocs/local-md, @fumadocs/mdx-remote, or a custom source | | fumadocs-ui | Opinionated theme: RootProvider, layouts, MDX defaults, search dialog (Tailwind v4) | building a fully bespoke UI on top of fumadocs-core |
Server-first via React Server Components. Static export is opt-in. Edge runtime is unsupported.
Step 1: Identify the task class
Map the user's request to one of these classes, then load the matching reference:
| Class | Trigger phrases | Reference | | --- | --- | --- | | Bootstrap a project | "scaffold", "set up Fumadocs", "install in <framework>", "create-fumadocs-app" | references/01-install-and-setup.md | | Routing / file conventions | meta.json, pages directives, slug rules, root folders, page tree | references/02-source-and-page-tree.md | | MDX configuration | source.config.ts, defineDocs, defineCollections, async / dynamic mode, plugin order, typegen | references/03-mdx-pipeline.md | | UI layouts / components / MDX wiring | DocsLayout/Notebook/Flux/Home, RootProvider, mdx-components.tsx, theming, MDX overrides | references/04-ui-layouts-and-components.md | | Search / i18n | adapter selection, search dialog, locale routing, middleware | references/05-search-and-i18n.md | | OpenAPI / OG / AI / content sources / guides | <APIPage>, generateFiles, OG image route, llms.txt, Ask AI, local-md, mdx-remote, PDF/EPUB/RSS | references/06-integrations.md | | Debugging an existing setup | "broken", "blank sidebar", "404", "build fails", "weird hydration" | references/07-pitfalls.md |
If the task spans multiple classes, load the references in the order shown above (setup → tree → MDX → UI → search/i18n → integrations).
Step 2: Confirm the package surface in use
Before recommending code, check which Fumadocs surface the project actually uses. Read these files (paths are conventional, not absolute):
package.json — confirm fumadocs-core, fumadocs-mdx, fumadocs-ui versions and which adapter is installed (next, @react-router/..., @tanstack/react-start, waku).source.config.ts (Fumadocs MDX) or lib/source.ts (low-level loader()) — determines whether the project uses the bundler-backed MDX path or a runtime source (local-md, mdx-remote, custom).app/<segment>/[[...slug]]/page.tsx; React Router: routes/docs/$.tsx or route('docs/*', ...); TanStack Start: routes/docs/$.tsx; Waku: pages/docs/[...slugs].tsx.fumadocs-ui/provider/{next,react-router,tanstack,waku} MUST match the framework.fumadocs-ui/css/<theme>.css) and fumadocs-ui/css/preset.css imports.If any of these contradicts the user's request, surface the conflict before editing.
Step 3: Choose adapters and modes deterministically
Use these decision tables instead of guessing:
collections/{server,browser,dynamic})| Choose | When | | --- | --- | | server | RSC / SSR / SSG (default, fastest first paint) | | browser | Client-routed apps (TanStack Start, React Router SPA mode) — only doc / docs collections | | dynamic | Very large libraries (>500 MDX) where build time / memory dominates | | direct import | One-off MDX as a page or React component (no loader() indirection) |
| Choose | When | | --- | --- | | Fumadocs MDX (default) | Bundler-backed, typegen, image optimization, full MDX import/export | | @fumadocs/local-md | Runtime-only, Cloudflare Workers compat, no eval, no bundler step | | @fumadocs/mdx-remote | Runtime compile from CMS / remote content (trusted input only) | | custom (StaticSource / DynamicSource) | Hardcoded trees, generated content, multi-tenant per-permission sources |
| Choose | When | | --- | --- | | orama (default) | Self-hosted, free, typed schema, vector capable | | flexsearch | Tiny / medium docs, zero infra, smaller bundle than Orama | | algolia | Enterprise scale, polished relevance (free tier requires logo) | | orama-cloud | Hosted Orama, scale without ops | | mixedbread | Semantic / vector / natural-language queries | | typesense | OSS scale + faceting (community adapter) | | trieve | RAG / hybrid retrieval (community-maintained) | | custom | Hand-roll a /static.json route from source.getPages().structuredData |
| Choose | When | | --- | --- | | fumadocs-ui/layouts/docs | Classic docs site with persistent sidebar | | fumadocs-ui/layouts/notebook | Denser, app-shell feel; supports top-tab navigation | | fumadocs-ui/layouts/flux | Aggressively minimal (client-only — no unserialisable RSC props) | | fumadocs-ui/layouts/home | Marketing / landing pages that share docs chrome |
When the layout changes, the per-doc page import must follow (fumadocs-ui/layouts/<layout>/page → DocsPage, DocsTitle, DocsBody).
Step 4: Wire source.config.ts, loader(), and mdx-components.tsx
These three files form the spine of any Fumadocs setup. Use the canonical templates (read first, then adapt):
assets/source-config-template.ts for the defineDocs + defineCollections + defineConfig shape, schema extension via pageSchema / metaSchema, and the recommended tsconfig alias collections/* → .source/*.assets/source-template.ts for the loader({ source, baseUrl, url, slugs, icon, i18n, plugins }) shape and the typical lib/source.ts exports.assets/mdx-components-template.tsx for the getMDXComponents pattern, the pre ref-strip workaround, and createRelativeLink(source, page) wiring.If the project already has these files, prefer surgical edits over rewrites — collection-level mdxOptions wipes globals, so use applyMdxPreset(...) to keep the docs preset.
Step 5: Validate before claiming done
For any change that touches MDX content, source config, page conventions, or layouts:
.source/ on dev/build; otherwise run npx fumadocs-mdx)..source/index.d.ts must compile cleanly. A .source/ not regenerated is the most common cause of "module not found" or "property does not exist" errors.meta.json.pages, breadcrumbs/TOC populate, search dialog opens with ⌘K. Layout-level breakage is silent until rendered.defaultLanguage and at least one non-default. hideLocale: 'always' cookies break static caches — surface this if applicable.Step 6: Use the pitfall catalog before debugging
When the user reports a bug, read references/07-pitfalls.md first — most failures are catalogued. The five most common:
fumadocs-ui/css/preset.css is supported.next.config.js (CJS) won't load fumadocs-mdx — the loader is ESM only; rename to next.config.mjs or enable Native Node TS resolver.DocsPage from layouts/<layout>/page; defaulting to layouts/docs/page produces broken TOC/footer.pathname only; any duplicate corrupts active-link detection.fumadocs-ui/provider/<framework> must match the adapter or RootProvider silently no-ops (search, theme switch, i18n all stop working).package.json. Ask which adapter they're on before generating code; never assume Next.js because the docs default to it.references/07-pitfalls.md for migration notes before editing.source.config.ts and a runtime source (local-md / mdx-remote) coexist. Two competing loaders is not a supported state — read references/06-integrations.md#content-sources and pick one before changing code.static (Orama static, FlexSearch static, or hosted/cloud) — server-fetch search will not work statically. See references/05-search-and-i18n.md#static-export..source/ does not exist or is stale. Trigger source generation before any typecheck; missing entries cause cryptic "module not found" / "is not exported" errors.openapiSource() mutates page.type to 'openapi' — every consumer (getLLMText, page renderer, search index, OG image, RSS) must branch on it. Read references/06-integrations.md#openapi before wiring.| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-04 | pass→fail | 18,788 | 22,351 | +19% | 1 | 1 | 0% | 2,449 | 5,449 | +122% | 0 | 0 | — |
case-01 | fail→pass | 22,638 | 30,793 | +36% | 1 | 1 | 0% | 3,867 | 7,528 | +95% | 0 | 0 | — |
case-02 | fail→fail | 30,611 | 34,953 | +14% | 1 | 1 | 0% | 4,361 | 8,253 | +89% | 0 | 0 | — |
case-03 | fail→pass | 42,731 | 34,637 | -19% | 1 | 1 | 0% | 7,219 | 8,438 | +17% | 0 | 0 | — |
case-05 | pass→pass | 18,123 | 20,860 | +15% | 1 | 1 | 0% | 2,765 | 5,465 | +98% | 0 | 0 | — |
case-06 | pass→pass | 15,773 | 20,283 | +29% | 1 | 1 | 0% | 2,907 | 5,368 | +85% | 0 | 0 | — |
case-07 | fail→pass | 29,051 | 17,355 | -40% | 1 | 1 | 0% | 4,568 | 4,711 | +3% | 0 | 0 | — |
case-08 | pass→pass | 29,180 | 15,966 | -45% | 1 | 1 | 0% | 3,795 | 5,261 | +39% | 0 | 0 | — |
case-09 | fail→pass | 5,690 | 10,157 | +79% | 1 | 1 | 0% | 1,032 | 3,421 | +231% | 0 | 0 | — |
case-10 | pass→pass | 9,909 | 14,143 | +43% | 1 | 1 | 0% | 1,674 | 4,205 | +151% | 0 | 0 | — |
case-11 | fail→pass | 16,938 | 14,895 | -12% | 1 | 1 | 0% | 2,440 | 4,025 | +65% | 0 | 0 | — |
case-12 | fail→pass | 18,594 | 9,208 | -50% | 1 | 1 | 0% | 2,132 | 4,349 | +104% | 0 | 0 | — |
case-13 | fail→fail | 17,992 | 20,795 | +16% | 1 | 1 | 0% | 1,981 | 5,242 | +165% | 0 | 0 | — |
case-14 | fail→pass | 20,453 | 10,514 | -49% | 1 | 1 | 0% | 2,846 | 3,632 | +28% | 0 | 0 | — |
case-15 | fail→pass | 22,888 | 23,375 | +2% | 1 | 1 | 0% | 2,876 | 5,678 | +97% | 0 | 0 | — |
case-16 | fail→pass | 17,942 | 12,561 | -30% | 1 | 1 | 0% | 2,072 | 3,869 | +87% | 0 | 0 | — |
case-17 | fail→pass | 19,992 | 8,301 | -58% | 1 | 1 | 0% | 2,217 | 4,016 | +81% | 0 | 0 | — |
case-18 | pass→pass | 23,442 | 22,306 | -5% | 1 | 1 | 0% | 2,766 | 5,157 | +86% | 0 | 0 | — |
case-19 | pass→pass | 25,478 | 29,374 | +15% | 1 | 1 | 0% | 3,375 | 6,544 | +94% | 0 | 0 | — |
case-20 | pass→pass | 16,390 | 9,614 | -41% | 1 | 1 | 0% | 1,777 | 3,343 | +88% | 0 | 0 | — |
case-21 | fail→pass | 20,128 | 10,210 | -49% | 1 | 1 | 0% | 2,565 | 3,388 | +32% | 0 | 0 | — |
case-22 | fail→pass | 25,279 | 9,220 | -64% | 1 | 1 | 0% | 2,698 | 3,177 | +18% | 0 | 0 | — |
case-23 | fail→pass | 27,782 | 23,962 | -14% | 1 | 1 | 0% | 3,771 | 5,850 | +55% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 23 cases were attempted. The headline lift of +52 percentage points is the difference between those two pass rates over the 23 comparable cases. 1 case got worse with the skill loaded, and it is included in that figure.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.