Fumadocs
"fumadocs documentation framework: Next.js App Router native, MDX content collections, full-text search, OpenAPI integration, TypeScript-first, customizable UI components, and content source adapters."
fumadocs is a modern documentation framework built natively on Next.js App Router. Unlike Nextra (which originated on Pages Router), fumadocs embraces React Server Components, the `app/` directory structure, and Next.js conventions from the ground up. It separates concerns into three packages: `fumadocs-core` (headless utilities), `fumadocs-ui` (styled components), and `fumadocs-mdx` (MDX content source). This modular architecture lets you use the full UI kit for rapid setup or just the core utilities when you need complete control. Content is defined through type-safe collections with Zod validation, and the framework provides built-in search (Orama or Algolia), OpenAPI page generation, and i18n support. ## Key Points - Use `fumadocs-mdx` as the content source for most projects. It handles MDX compilation, frontmatter validation, and hot reload out of the box. - Leverage `source.pageTree` for sidebar generation — it respects `meta.json` ordering and nesting, eliminating manual navigation configuration. - Use the built-in Orama search for static sites. Switch to Algolia only when you need server-side search across thousands of pages. - Define `generateStaticParams` and `generateMetadata` in your docs page component — fumadocs provides helper methods (`source.generateParams()`) that handle this cleanly. - Use `meta.json` in each content directory to control page ordering and section titles. The `---` separator creates visual groupings in the sidebar. - Import `defaultMdxComponents` from `fumadocs-ui/mdx` and spread them into your MDX component map to get styled headings, code blocks, tables, and links without extra configuration. - Use the OpenAPI integration to auto-generate API reference pages from your spec, keeping docs in sync with your API. - Run `fumadocs-openapi` generation as a build script rather than at runtime to keep build times predictable. - **Using fumadocs with Pages Router**: fumadocs is built for App Router. Attempting to use it with Pages Router leads to broken layouts and missing features. Use Nextra for Pages Router projects. - **Ignoring `source.config.ts`**: This file defines your content collections. Skipping schema validation means frontmatter errors slip through to production. - **Overriding fumadocs-ui styles with global CSS**: Use the theme configuration and CSS variables (`--fd-*`) provided by fumadocs-ui. Global overrides break when the library updates. - **Creating custom search from scratch**: fumadocs provides `createSearchAPI` for the server and a pre-built search dialog for the client. Building your own duplicates tested functionality. ## Quick Example ```bash npx create-fumadocs-app my-docs # or manual installation: npm install fumadocs-ui fumadocs-core fumadocs-mdx ``` ```bash npm install fumadocs-openapi ```
skilldb get seo-content-skills/FumadocsFull skill: 409 linesfumadocs — Next.js Documentation Framework
Core Philosophy
fumadocs is a modern documentation framework built natively on Next.js App Router. Unlike Nextra (which originated on Pages Router), fumadocs embraces React Server Components, the app/ directory structure, and Next.js conventions from the ground up. It separates concerns into three packages: fumadocs-core (headless utilities), fumadocs-ui (styled components), and fumadocs-mdx (MDX content source). This modular architecture lets you use the full UI kit for rapid setup or just the core utilities when you need complete control. Content is defined through type-safe collections with Zod validation, and the framework provides built-in search (Orama or Algolia), OpenAPI page generation, and i18n support.
Setup
Quick Start
npx create-fumadocs-app my-docs
# or manual installation:
npm install fumadocs-ui fumadocs-core fumadocs-mdx
Project Structure
my-docs/
├── app/
│ ├── layout.tsx
│ ├── page.tsx
│ ├── docs/
│ │ ├── layout.tsx
│ │ └── [[...slug]]/
│ │ └── page.tsx
│ └── api/
│ └── search/
│ └── route.ts
├── content/
│ └── docs/
│ ├── index.mdx
│ ├── getting-started.mdx
│ └── guides/
│ ├── meta.json
│ └── installation.mdx
├── source.config.ts
├── lib/
│ └── source.ts
└── next.config.mjs
Content Source Configuration
// source.config.ts
import { defineDocs, defineConfig } from "fumadocs-mdx/config";
export const { docs, meta } = defineDocs({
dir: "content/docs",
});
export default defineConfig();
// lib/source.ts
import { loader } from "fumadocs-core/source";
import { createMDXSource } from "fumadocs-mdx";
import { docs, meta } from "@/.source";
export const source = loader({
baseUrl: "/docs",
source: createMDXSource(docs, meta),
});
// next.config.mjs
import { createMDX } from "fumadocs-mdx/next";
const withMDX = createMDX();
/** @type {import('next').NextConfig} */
const config = {
reactStrictMode: true,
};
export default withMDX(config);
Root Layout
// app/layout.tsx
import { RootProvider } from "fumadocs-ui/provider";
import type { ReactNode } from "react";
import "fumadocs-ui/style.css";
export default function RootLayout({ children }: { children: ReactNode }) {
return (
<html lang="en" suppressHydrationWarning>
<body>
<RootProvider>{children}</RootProvider>
</body>
</html>
);
}
Docs Layout and Page
// app/docs/layout.tsx
import { DocsLayout } from "fumadocs-ui/layouts/docs";
import type { ReactNode } from "react";
import { source } from "@/lib/source";
export default function Layout({ children }: { children: ReactNode }) {
return (
<DocsLayout
tree={source.pageTree}
nav={{
title: "My Docs",
url: "/docs",
}}
sidebar={{
defaultOpenLevel: 1,
}}
>
{children}
</DocsLayout>
);
}
// app/docs/[[...slug]]/page.tsx
import { source } from "@/lib/source";
import {
DocsPage,
DocsBody,
DocsTitle,
DocsDescription,
} from "fumadocs-ui/page";
import { notFound } from "next/navigation";
import defaultMdxComponents from "fumadocs-ui/mdx";
interface Props {
params: Promise<{ slug?: string[] }>;
}
export default async function Page({ params }: Props) {
const { slug } = await params;
const page = source.getPage(slug);
if (!page) notFound();
const MDX = page.data.body;
return (
<DocsPage
toc={page.data.toc}
lastUpdate={page.data.lastModified}
full={page.data.full}
>
<DocsTitle>{page.data.title}</DocsTitle>
<DocsDescription>{page.data.description}</DocsDescription>
<DocsBody>
<MDX components={{ ...defaultMdxComponents }} />
</DocsBody>
</DocsPage>
);
}
export function generateStaticParams() {
return source.generateParams();
}
export async function generateMetadata({ params }: Props) {
const { slug } = await params;
const page = source.getPage(slug);
if (!page) return {};
return {
title: page.data.title,
description: page.data.description,
};
}
Key Techniques
Search with Orama
// app/api/search/route.ts
import { source } from "@/lib/source";
import { createSearchAPI } from "fumadocs-core/search/server";
export const { GET } = createSearchAPI("advanced", {
indexes: source.getPages().map((page) => ({
title: page.data.title,
description: page.data.description,
url: page.url,
id: page.url,
structuredData: page.data.structuredData,
})),
});
// app/layout.tsx — enable search dialog
import { RootProvider } from "fumadocs-ui/provider";
import { ReactNode } from "react";
import "fumadocs-ui/style.css";
export default function RootLayout({ children }: { children: ReactNode }) {
return (
<html lang="en" suppressHydrationWarning>
<body>
<RootProvider
search={{
options: {
type: "fetch",
},
}}
>
{children}
</RootProvider>
</body>
</html>
);
}
Navigation with meta.json
// content/docs/guides/meta.json
{
"title": "Guides",
"pages": [
"installation",
"configuration",
"deployment",
"---",
"troubleshooting"
]
}
OpenAPI Integration
npm install fumadocs-openapi
// lib/source.ts — with OpenAPI
import { loader } from "fumadocs-core/source";
import { createMDXSource } from "fumadocs-mdx";
import { createOpenAPI } from "fumadocs-openapi/server";
import { docs, meta } from "@/.source";
export const source = loader({
baseUrl: "/docs",
source: createMDXSource(docs, meta),
});
export const openapi = createOpenAPI({
source: "./openapi.yaml",
});
// scripts/generate-openapi.mts
import { generateFiles } from "fumadocs-openapi";
void generateFiles({
input: ["./openapi.yaml"],
output: "./content/docs/api",
per: "operation",
render: (title, description) => {
return {
frontmatter: {
title,
description: description ?? "",
full: true,
},
};
},
});
Custom MDX Components
// components/install-tabs.tsx
import { Tab, Tabs } from "fumadocs-ui/components/tabs";
interface InstallTabsProps {
package: string;
}
export function InstallTabs({ package: pkg }: InstallTabsProps) {
return (
<Tabs items={["npm", "pnpm", "yarn", "bun"]}>
<Tab value="npm">
```bash
npm install {pkg}
```
</Tab>
<Tab value="pnpm">
```bash
pnpm add {pkg}
```
</Tab>
<Tab value="yarn">
```bash
yarn add {pkg}
```
</Tab>
<Tab value="bun">
```bash
bun add {pkg}
```
</Tab>
</Tabs>
);
}
i18n Configuration
// lib/source.ts — internationalized
import { loader } from "fumadocs-core/source";
import { createMDXSource } from "fumadocs-mdx";
import { i18n } from "@/lib/i18n";
import { docs, meta } from "@/.source";
export const source = loader({
baseUrl: "/docs",
source: createMDXSource(docs, meta),
i18n,
});
// lib/i18n.ts
import type { I18nConfig } from "fumadocs-core/i18n";
export const i18n: I18nConfig = {
defaultLanguage: "en",
languages: ["en", "es", "ja"],
};
Content MDX with Frontmatter
---
title: Getting Started
description: Learn how to set up fumadocs in your Next.js project
icon: Rocket
---
import { Callout } from 'fumadocs-ui/components/callout'
import { Card, Cards } from 'fumadocs-ui/components/card'
## Prerequisites
<Callout title="Requirement" type="warn">
fumadocs requires Next.js 14+ with App Router enabled.
</Callout>
## Next Steps
<Cards>
<Card
title="Configuration"
description="Customize your documentation site"
href="/docs/guides/configuration"
/>
<Card
title="Deployment"
description="Deploy to production"
href="/docs/guides/deployment"
/>
</Cards>
Best Practices
- Use
fumadocs-mdxas the content source for most projects. It handles MDX compilation, frontmatter validation, and hot reload out of the box. - Leverage
source.pageTreefor sidebar generation — it respectsmeta.jsonordering and nesting, eliminating manual navigation configuration. - Use the built-in Orama search for static sites. Switch to Algolia only when you need server-side search across thousands of pages.
- Define
generateStaticParamsandgenerateMetadatain your docs page component — fumadocs provides helper methods (source.generateParams()) that handle this cleanly. - Use
meta.jsonin each content directory to control page ordering and section titles. The---separator creates visual groupings in the sidebar. - Import
defaultMdxComponentsfromfumadocs-ui/mdxand spread them into your MDX component map to get styled headings, code blocks, tables, and links without extra configuration. - Use the OpenAPI integration to auto-generate API reference pages from your spec, keeping docs in sync with your API.
- Run
fumadocs-openapigeneration as a build script rather than at runtime to keep build times predictable.
Anti-Patterns
- Using fumadocs with Pages Router: fumadocs is built for App Router. Attempting to use it with Pages Router leads to broken layouts and missing features. Use Nextra for Pages Router projects.
- Ignoring
source.config.ts: This file defines your content collections. Skipping schema validation means frontmatter errors slip through to production. - Overriding fumadocs-ui styles with global CSS: Use the theme configuration and CSS variables (
--fd-*) provided by fumadocs-ui. Global overrides break when the library updates. - Creating custom search from scratch: fumadocs provides
createSearchAPIfor the server and a pre-built search dialog for the client. Building your own duplicates tested functionality. - Deeply nesting content directories: Like Nextra, more than 3 levels of nesting creates navigation friction. Flatten the structure and use
meta.jsontitles to provide context. - Skipping
generateStaticParams: Without it, docs pages are dynamically rendered on every request. Always pre-render documentation at build time for performance. - Mixing fumadocs-ui with another component library: fumadocs-ui components are designed to work together with consistent spacing, colors, and dark mode. Mixing in Chakra, MUI, or similar libraries creates visual inconsistency.
- Not using
full: truefor API reference pages: Wide content like API tables and request/response examples need full-width layout. Setfull: truein frontmatter for these pages.
Install this skill directly: skilldb add seo-content-skills
Related Skills
Contentlayer
"Contentlayer and Velite for type-safe content management: transforming Markdown/MDX into typed data, schema validation, computed fields, Next.js integration, hot reload, and migration between content tools."
Core Web Vitals
Core Web Vitals optimization: LCP, INP, and CLS measurement, diagnosis, and improvement strategies for better search rankings and user experience.
Mdx
"MDX authoring with Next.js: Markdown + JSX, custom components, frontmatter extraction, @next/mdx, mdx-bundler, contentlayer integration, rehype/remark plugins, and syntax highlighting with Shiki or Prism."
Next SEO
"Next.js SEO and metadata management: meta tags, Open Graph, Twitter cards, JSON-LD structured data, canonical URLs, robots directives, and sitemap generation using the Metadata API and next-seo."
Nextra
"Nextra documentation framework: MDX-powered Next.js docs and blog sites, sidebar navigation, full-text search, i18n, themes (docs and blog), frontmatter configuration, and custom components."
Programmatic SEO
Programmatic SEO strategies: generating thousands of search-optimized pages from structured data, template design, internal linking, and indexing at scale.