Manual Installation
Add Fumadocs to existing projects.
Before continuing, make sure:
- Next.js 15 and Tailwind CSS 4 are configured.
Getting Started
npm install fumadocs-ui fumadocs-core
pnpm add fumadocs-ui fumadocs-core
yarn add fumadocs-ui fumadocs-core
bun add fumadocs-ui fumadocs-core
MDX Components
import defaultMdxComponents from 'fumadocs-ui/mdx';
import type { MDXComponents } from 'mdx/types';
export function getMDXComponents(components?: MDXComponents): MDXComponents {
return {
...defaultMdxComponents,
...components,
};
}
Content Source
Fumadocs supports different content sources, including Fumadocs MDX and Content Collections.
Fumadocs MDX is our official content source, you can configure it with:
npm install fumadocs-mdx @types/mdx
pnpm add fumadocs-mdx @types/mdx
yarn add fumadocs-mdx @types/mdx
bun add fumadocs-mdx @types/mdx
import { createMDX } from 'fumadocs-mdx/next';
const withMDX = createMDX();
/** @type {import('next').NextConfig} */
const config = {
reactStrictMode: true,
};
export default withMDX(config);
import { defineDocs } from 'fumadocs-mdx/config';
export const docs = defineDocs({
dir: 'content/docs',
});
{
"scripts": {
"postinstall": "fumadocs-mdx"
}
}
Finally, to access your content:
// .source folder will be generated when you run `next dev`
import { docs } from '@/.source';
import { loader } from 'fumadocs-core/source';
export const source = loader({
baseUrl: '/docs',
source: docs.toFumadocsSource(),
});
Root Layout
Wrap the entire application inside Root Provider, and add required styles to body
.
import { RootProvider } from 'fumadocs-ui/provider';
import type { ReactNode } from 'react';
export default function Layout({ children }: { children: ReactNode }) {
return (
<html lang="en" suppressHydrationWarning>
<body
// you can use Tailwind CSS too
style={{
display: 'flex',
flexDirection: 'column',
minHeight: '100vh',
}}
>
<RootProvider>{children}</RootProvider>
</body>
</html>
);
}
Styles
Add the following Tailwind CSS styles to global.css
.
@import 'tailwindcss';
@import 'fumadocs-ui/css/neutral.css';
@import 'fumadocs-ui/css/preset.css';
It doesn't come with a default font, you may choose one from next/font
.
Layout
Create a app/layout.config.tsx
file to put the shared options for our layouts.
import { BaseLayoutProps } from 'fumadocs-ui/layouts/shared';
export const baseOptions: BaseLayoutProps = {
nav: {
title: 'My App',
},
};
Create a folder /app/docs
for our docs, and give it a proper layout.
import { source } from '@/lib/source';
import { DocsLayout } from 'fumadocs-ui/layouts/docs';
import type { ReactNode } from 'react';
import { baseOptions } from '@/app/layout.config';
export default function Layout({ children }: { children: ReactNode }) {
return (
<DocsLayout tree={source.pageTree} {...baseOptions}>
{children}
</DocsLayout>
);
}
pageTree
refers to Page Tree, it should be provided by your content source.
Page
Create a catch-all route /app/docs/[[...slug]]
for docs pages.
In the page, wrap your content in the Page component.
import { source } from '@/lib/source';
import {
DocsBody,
DocsDescription,
DocsPage,
DocsTitle,
} from 'fumadocs-ui/page';
import { notFound } from 'next/navigation';
import { getMDXComponents } from '@/mdx-components';
export default async function Page(props: {
params: Promise<{ slug?: string[] }>;
}) {
const params = await props.params;
const page = source.getPage(params.slug);
if (!page) notFound();
const MDX = page.data.body;
return (
<DocsPage toc={page.data.toc} full={page.data.full}>
<DocsTitle>{page.data.title}</DocsTitle>
<DocsDescription>{page.data.description}</DocsDescription>
<DocsBody>
<MDX components={getMDXComponents()} />
</DocsBody>
</DocsPage>
);
}
export async function generateStaticParams() {
return source.generateParams();
}
export async function generateMetadata(props: {
params: Promise<{ slug?: string[] }>;
}) {
const params = await props.params;
const page = source.getPage(params.slug);
if (!page) notFound();
return {
title: page.data.title,
description: page.data.description,
};
}
import { source } from '@/lib/source';
import type { Metadata } from 'next';
import {
DocsPage,
DocsBody,
DocsTitle,
DocsDescription,
} from 'fumadocs-ui/page';
import { notFound } from 'next/navigation';
import { MDXContent } from '@content-collections/mdx/react';
import { getMDXComponents } from '@/mdx-components';
export default async function Page(props: {
params: Promise<{ slug?: string[] }>;
}) {
const params = await props.params;
const page = source.getPage(params.slug);
if (!page) {
notFound();
}
return (
<DocsPage toc={page.data.toc} full={page.data.full}>
<DocsTitle>{page.data.title}</DocsTitle>
<DocsDescription>{page.data.description}</DocsDescription>
<DocsBody>
<MDXContent code={page.data.body} components={getMDXComponents()} />
</DocsBody>
</DocsPage>
);
}
export async function generateStaticParams() {
return source.generateParams();
}
export async function generateMetadata(props: {
params: Promise<{ slug?: string[] }>;
}) {
const params = await props.params;
const page = source.getPage(params.slug);
if (!page) notFound();
return {
title: page.data.title,
description: page.data.description,
} satisfies Metadata;
}
Search
Use the default document search based on Orama.
import { source } from '@/lib/source';
import { createFromSource } from 'fumadocs-core/search/server';
export const { GET } = createFromSource(source);
Learn more about Document Search.
Done
You can start the dev server and create MDX files.
---
title: Hello World
---
## Introduction
I love Anime.
Deploying
It should work out-of-the-box with Vercel & Netlify.
Cloudflare
Use https://opennext.js.org/cloudflare, Fumadocs doesn't work on Edge runtime.
Docker Deployment
If you want to deploy your Fumadocs app using Docker with Fumadocs MDX configured, make sure to add the source.config.ts
file to the WORKDIR
in the Dockerfile.
The following snippet is taken from the official Next.js Dockerfile Example:
WORKDIR /app
# Install dependencies based on the preferred package manager
COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* .npmrc* source.config.ts ./
This ensures Fumadocs MDX can access your configuration file during builds.
How is this guide?