Fumadocs

Manual Installation

Create a new fumadocs project from scratch.

Make sure to read the Quick Start guide first.

Getting Started

Create a new Next.js application with create-next-app, and install required packages.

npm install fumadocs-ui fumadocs-core

Content Source

You can choose a content source you prefer.

There is a list of supported sources:

You have to configure the library correctly following their setup guide before continuing.

Make sure to create a source.ts file for content adapters, this allows Fumadocs to interact with your content source through a unified interface.

lib/source.ts
import { docs, meta } from '@/.source';
import { createMDXSource } from 'fumadocs-mdx';
import { loader } from 'fumadocs-core/source';
 
export const source = loader({
  baseUrl: '/docs',
  source: createMDXSource(docs, meta),
});

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

Setup Tailwind CSS on your Next.js app, and add the official Tailwind CSS plugin.

tailwind.config.js
import { createPreset } from 'fumadocs-ui/tailwind-plugin';
 
/** @type {import('tailwindcss').Config} */
export default {
  darkMode: 'class',
  presets: [createPreset()],
  content: [
    './node_modules/fumadocs-ui/dist/**/*.js',
 
    './components/**/*.{ts,tsx}',
    './app/**/*.{ts,tsx}',
    './content/**/*.mdx',
    './mdx-components.tsx',
  ],
};

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. It may vary depending on your content source. You should configure static rendering with generateStaticParams and metadata with generateMetadata.

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';
import { metadataImage } from '@/lib/metadata';
 
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={{ ...defaultMdxComponents }} />
      </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 metadataImage.withImage(page.slugs, {
    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.

Content

See the docs of your content source (e.g. Fumadocs MDX) to customise how content are processed.

Routing

See Organizing Pages for customising routing.

Other Pages

You can use Home Layout for other pages of the site. It includes the theme toggle and global navigation links.

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 Exmaple:

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.

Edit on GitHub

Last updated on

On this page