Beautiful Documentation with Fumadocs
20. December, 2025 • 10 min read • Develop
The Next.js-Native Docs Framework
Documentation is often an afterthought in software projects, bolted on with tools that don't quite fit the rest of your stack. Fumadocs changes this by providing a documentation framework that's built natively for Next.js, using React Server Components, Tailwind CSS v4, and type-safe MDX content.
With over 11,000 GitHub stars and adoption by projects like shadcn/ui, Zod, Prisma, and Vercel’s own tools, Fumadocs has quickly established itself as the modern choice for documentation in the React ecosystem. In this post, we’ll explore its architecture, set it up, and walk through the features that make it stand out.
Why Fumadocs?
The documentation framework landscape has several established players. Docusaurus, backed by Meta, is the most popular with 58k+ stars. Nextra provides a Next.js-native alternative. Mintlify offers a hosted SaaS solution. So why consider Fumadocs?
The key differentiator is its headless-first architecture combined with a native App Router design. Docusaurus uses client-side routing and its own build system — it doesn’t integrate with your existing Next.js project. Nextra originated in the Pages Router era and has been adapting to the App Router. Fumadocs was built from the ground up for React Server Components, meaning your documentation compiles to minimal client-side JavaScript and benefits from all the performance features of the modern Next.js stack.
Other standout features include built-in OpenAPI documentation rendering, type-safe content collections with Zod validation, and seamless compatibility with shadcn/ui’s design system. If your project already uses Next.js and Tailwind, Fumadocs feels like a natural extension rather than a separate tool.
Architecture
Fumadocs is split into four packages, each serving a specific purpose:
- fumadocs-core — The headless foundation. Provides search adapters, content source integrations, and Markdown utilities. Framework-agnostic within the React ecosystem.
- fumadocs-ui — The styled theme layer. A complete set of UI components (sidebar, table of contents, breadcrumbs, code blocks, callouts) built with Tailwind CSS v4.
- fumadocs-mdx — The content layer. Transforms MDX files into type-safe data with validated frontmatter, auto-generated table of contents, and structured search data.
- Fumadocs CLI — Scaffolding tool for project setup and component installation.
This separation means you can use just fumadocs-core as a headless library and build your own UI, or use fumadocs-ui for a batteries-included experience. Most projects start with the full stack and customize from there.
Getting Started
Setting up Fumadocs is quick with the CLI:
npm create fumadocs-appThis scaffolds a Next.js project with Fumadocs pre-configured. For an existing project, install the packages manually:
npm install fumadocs-core fumadocs-ui fumadocs-mdxContent Configuration
Define your content collections in a source.config.ts file:
// source.config.ts
import { defineDocs } from 'fumadocs-mdx/config';
import { z } from 'zod';
export const docs = defineDocs({
dir: 'content/docs',
docs: {
schema: {
description: z.string().optional(),
category: z.enum(['guides', 'api', 'tutorials']).optional(),
},
},
});This tells Fumadocs where your MDX files live and validates their frontmatter with Zod at build time. If a frontmatter field doesn’t match the schema, you get a clear error during development — not a mysterious runtime crash.
Create the Loader
The loader transforms your content into a structured API:
// lib/source.ts
import { loader } from 'fumadocs-core/source';
import { docs } from '@/.source';
export const source = loader({
baseUrl: '/docs',
source: docs,
});Set Up the Layout
// app/docs/layout.tsx
import { DocsLayout } from 'fumadocs-ui/layouts/docs';
import { source } from '@/lib/source';
import type { ReactNode } from 'react';
export default function Layout({ children }: { children: ReactNode }) {
return (
<DocsLayout
tree={source.getPageTree()}
nav={{ title: 'My Docs' }}
sidebar={{ defaultOpenLevel: 1 }}
>
{children}
</DocsLayout>
);
}Create the Page
// app/docs/[[...slug]]/page.tsx
import { source } from '@/lib/source';
import { DocsPage, DocsBody } from 'fumadocs-ui/page';
import { notFound } from 'next/navigation';
export default async function Page({
params,
}: {
params: Promise<{ slug?: string[] }>;
}) {
const { slug } = await params;
const page = source.getPage(slug);
if (!page) notFound();
const MDX = page.data.body;
return (
<DocsPage toc={page.data.toc}>
<DocsBody>
<MDX />
</DocsBody>
</DocsPage>
);
}
export function generateStaticParams() {
return source.generateParams();
}Now place MDX files in content/docs/ and they’ll appear as documentation pages with sidebar navigation, table of contents, and breadcrumbs — all generated automatically.
MDX Features
Fumadocs extends MDX with several features that make documentation writing more productive.
Syntax Highlighting
Code blocks use Shiki for syntax highlighting with support for line highlighting, word highlighting, titles, and copy buttons:
```typescript title="api/users.ts" {3-5}
import { db } from '@/lib/db';
export async function getUsers() {
return db.user.findMany();
}
```The highlighting runs at build time as part of the MDX compilation, so there’s no client-side JavaScript overhead for syntax coloring.
Callouts
Use admonition-style callouts for tips, warnings, and errors:
<Callout type="info">
This is an informational note for readers.
</Callout>
<Callout type="warn">
Be careful with this approach in production.
</Callout>Tabs
Create tabbed content with shared state across the page:
<Tabs groupId="package-manager" items={['npm', 'pnpm', 'yarn']}>
<Tab value="npm">npm install fumadocs-ui</Tab>
<Tab value="pnpm">pnpm add fumadocs-ui</Tab>
<Tab value="yarn">yarn add fumadocs-ui</Tab>
</Tabs>When a user selects “pnpm” in one tab group, all other tab groups with the same groupId switch to “pnpm” as well. The selection persists across page navigations via sessionStorage.
Cards and Steps
Structure your content with visual components:
<Steps>
<Step>Install the dependencies</Step>
<Step>Configure your project</Step>
<Step>Start writing docs</Step>
</Steps>
<Cards>
<Card title="Getting Started" href="/docs/quickstart">
Set up Fumadocs in your project
</Card>
<Card title="Components" href="/docs/components">
Browse available UI components
</Card>
</Cards>Search
Fumadocs offers three search backends, all integrated through a unified UI with a Cmd+K / Ctrl+K hotkey:
Orama (Recommended)
The default search engine. Self-hosted, free, and requires no external services:
// app/api/search/route.ts
import { source } from '@/lib/source';
import { createFromSource } from 'fumadocs-core/search/server';
export const { GET } = createFromSource(source);That’s the entire search setup — Fumadocs automatically indexes your content and provides a search dialog with highlighted results.
Algolia
For large documentation sites that need more advanced search features:
import { createAlgoliaFromSource } from 'fumadocs-core/search/algolia';
const client = createAlgoliaFromSource(source, {
appId: process.env.ALGOLIA_APP_ID!,
apiKey: process.env.ALGOLIA_API_KEY!,
indexName: 'docs',
});Fumadocs creates one record per paragraph with searchable title, section, and content fields, and provides a sync script to upload indexes after each build.
Flexsearch
A lightweight alternative for smaller sites that want fast, client-side search without any API routes.
OpenAPI Documentation
One of Fumadocs’ standout features is built-in OpenAPI documentation rendering. Most documentation frameworks require third-party plugins or manual setup for API reference pages. Fumadocs generates interactive API documentation directly from your OpenAPI/Swagger specifications.
This means you can maintain a single OpenAPI spec and have Fumadocs automatically render endpoint documentation with request/response schemas, parameter tables, and example payloads — all styled consistently with the rest of your docs.
TypeScript Documentation
The TypeTable component renders TypeScript type information as structured tables:
import { TypeTable } from 'fumadocs-ui/components/type-table';
<TypeTable
type={{
name: {
type: 'string',
description: 'The user display name',
required: true,
},
email: {
type: 'string',
description: 'Email address',
},
role: {
type: "'admin' | 'user'",
description: 'User role',
default: "'user'",
},
}}
/>This is particularly useful for library documentation where you need to document component props, configuration options, or API types in a readable format.
Theming and Customization
Fumadocs UI is built on Tailwind CSS v4 and offers extensive theming options. Eleven built-in color themes are available — neutral, black, vitepress, dusk, catppuccin, ocean, purple, solar, emerald, ruby, and aspen.
For custom theming, override CSS variables in your stylesheet:
@import 'tailwindcss';
@import 'fumadocs-ui/css/neutral.css';
@import 'fumadocs-ui/css/preset.css';
@theme {
--color-fd-primary: oklch(0.55 0.2 250);
--color-fd-background: oklch(0.98 0 0);
}If your project uses shadcn/ui, Fumadocs provides a dedicated stylesheet that adopts shadcn’s color system, ensuring your documentation matches the rest of your application’s design.
The layout supports RTL languages, customizable sidebar behavior (collapsible folders, auto-expanding levels), mobile-responsive navigation, and configurable page width via CSS custom properties.
Internationalization
Fumadocs supports multi-language documentation through its Source API. Configure i18n in the loader to generate separate page trees per locale:
export const source = loader({
baseUrl: '/docs',
source: docs,
i18n: {
languages: ['en', 'de', 'fr'],
defaultLanguage: 'en',
},
});When a translation is missing for a specific page, Fumadocs falls back to the default language. Search indexes are generated per locale, ensuring users search in their own language.
Static Generation and Performance
Fumadocs is designed with performance as a first-class concern. Since MDX compiles to React Server Components, documentation pages ship minimal client-side JavaScript. The content is rendered on the server and sent as HTML — only interactive components like the search dialog and sidebar toggles require JavaScript on the client.
The generateStaticParams() integration means all documentation pages can be statically generated at build time. Combined with static search indexes (both Orama and Flexsearch support pre-rendered indexes), you can deploy your entire documentation site as static files — no server runtime needed.
For sites with hundreds of pages, this approach scales well. The Source API operates with in-memory storage during the build, so there’s no database or external service dependency. Content is read from the filesystem, processed, and output as static HTML.
Sidebar and Navigation
The sidebar is automatically generated from your file structure. A content/docs/ directory with nested folders becomes a hierarchical navigation tree. You can customize the ordering and labels through meta.json files:
{
"title": "Getting Started",
"pages": ["installation", "quickstart", "configuration"]
}The sidebar supports collapsible folders with configurable default expansion levels, root-level tabs for organizing major sections (like “Guides” vs “API Reference”), and custom banners for announcements or version selectors.
On mobile, the sidebar transforms into a responsive navigation with a hamburger menu, breadcrumbs for context, and smooth animations — all handled by the layout component without additional configuration.
Fumadocs vs the Alternatives
| Fumadocs | Docusaurus | Nextra | |
|---|---|---|---|
| Framework | Next.js App Router | Custom React | Next.js |
| Rendering | RSC (minimal JS) | Client-side | Pages or App Router |
| Styling | Tailwind CSS v4 | CSS Modules | Tailwind or CSS |
| Content | Type-safe MDX | MDX | MDX |
| Search | Orama/Flexsearch/Algolia | Algolia | Flexsearch |
| OpenAPI | Built-in | Plugin required | No built-in |
| TypeScript docs | TypeTable component | Manual | Manual |
| Customization | Headless core + UI layer | Swizzling | Theme-based |
Fumadocs’ main trade-off is a smaller community compared to Docusaurus (11k vs 58k stars). However, its rapid adoption by high-profile projects — shadcn/ui, Zod, Prisma, Better Auth, and several Vercel tools — and active development pace suggest the gap is closing quickly. The project has accumulated over 5,200 commits and 1,580 releases, reflecting a commitment to continuous improvement.
If you’re already invested in the Next.js ecosystem with Tailwind CSS and potentially shadcn/ui, Fumadocs integrates far more naturally than alternatives that bring their own build systems and styling approaches. Your documentation inherits the same design language as your application, and you can share components between the two.
Conclusion
Fumadocs brings documentation into the modern React stack. By building natively on the Next.js App Router, React Server Components, and Tailwind CSS v4, it delivers documentation sites that are fast, type-safe, and visually consistent with your application. The headless architecture means you’re never locked into a specific design — use the batteries-included UI layer to ship quickly, or build a completely custom experience on top of the core.
Whether you’re documenting a component library, an API, or a product, Fumadocs provides the tools to create documentation that developers actually enjoy reading and writing.
‘Till next time!