Fumadocs

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

MDX Components

mdx-components.tsx
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
next.config.mjs
import { createMDX } from 'fumadocs-mdx/next';
 
const withMDX = createMDX();
 
/** @type {import('next').NextConfig} */
const config = {
  reactStrictMode: true,
};
 
export default withMDX(config);
source.config.ts
import { defineDocs } from 'fumadocs-mdx/config';
 
export const docs = defineDocs({
  dir: 'content/docs',
});

Add a postinstall script to generate types:

package.json
{
  "scripts": {
    "postinstall": "fumadocs-mdx"
  }
}

Finally, to access your content:

lib/source.ts
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 to global.css.

Tailwind 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.

app/layout.config.tsx
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.

app/docs/layout.tsx
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.

app/docs/[[...slug]]/page.tsx
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,
  };
}

Use the default document search based on Orama.

app/api/search/route.ts
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.

content/docs/index.mdx
---
title: Hello World
---
 
## Introduction
 
I love Anime.

Deploying

It should work out-of-the-box with Vercel & Netlify.

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:

Dockerfile
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?

On this page