Skip to main content
Migrate your existing documentation to Fumadocs and take advantage of modern features, better performance, and an improved developer experience.

Migrating from Other Frameworks

From Nextra

Nextra and Fumadocs share similar concepts, making migration straightforward:
1

Install Fumadocs

Replace Nextra with Fumadocs packages:
# Remove Nextra
npm uninstall nextra nextra-theme-docs

# Install Fumadocs
npm install fumadocs-ui fumadocs-core fumadocs-mdx
2

Update Next.js Configuration

Replace Nextra’s Next.js config:
next.config.mjs
import nextra from 'nextra';

const withNextra = nextra({
  theme: 'nextra-theme-docs',
});

import createMDX from 'fumadocs-mdx/config';

const withMDX = createMDX();

export default withMDX({
  // Your Next.js config
});
3

Create Source Configuration

source.config.ts
import { defineConfig, defineDocs } from 'fumadocs-mdx/config';

export const docs = defineDocs({
  dir: 'content/docs', // or 'pages' if using Nextra's default
});

export default defineConfig();
4

Move Content Files

Move your MDX files from pages/ to content/docs/:
mv pages content/docs
Or keep them in pages/ and update source.config.ts:
source.config.ts
export const docs = defineDocs({
  dir: 'pages',
});
5

Update Frontmatter

Convert Nextra frontmatter to Fumadocs format:
content/docs/index.mdx
---
display: hidden
type: page
title: Getting Started
description: Learn how to get started
---
Common conversions:
  • display: hidden → remove (use meta.json instead)
  • type: page → not needed
  • Nextra uses _meta.json, Fumadocs uses meta.json
6

Update Meta Files

Rename and convert _meta.json to meta.json:
content/docs/guides/meta.json
{
  "installation": "Installation",
  "title": "Guides",
  "configuration": "Configuration",
  "pages": ["installation", "configuration"]
}
7

Update Component Imports

Replace Nextra components:
import { Callout } from 'nextra/components';
import { Tabs, Tab } from 'nextra/components';

import { Callout } from 'fumadocs-ui/components/callout';
import { Tabs, Tab } from 'fumadocs-ui/components/tabs';

<Callout type="warning">
  This works the same way!
</Callout>

<Tabs items={['npm', 'pnpm']}>
  <Tab value="npm">npm install</Tab>
  <Tab value="pnpm">pnpm install</Tab>
</Tabs>
8

Set Up Layouts

Replace Nextra’s theme with Fumadocs layouts:
app/docs/layout.tsx
import { DocsLayout } from 'fumadocs-ui/layouts/docs';
import { source } from '@/lib/source';
import { baseOptions } from '@/lib/layout.shared';

export default function Layout({ children }: LayoutProps<'/docs'>) {
  return (
    <DocsLayout tree={source.getPageTree()} {...baseOptions()}>
      {children}
    </DocsLayout>
  );
}
app/docs/[[...slug]]/page.tsx
import { source } from '@/lib/source';
import { DocsBody, DocsPage, DocsTitle } from 'fumadocs-ui/layouts/docs/page';
import { notFound } from 'next/navigation';
import { getMDXComponents } from '@/mdx-components';

export default async function Page(props: PageProps) {
  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}>
      <DocsTitle>{page.data.title}</DocsTitle>
      <DocsBody>
        <MDX components={getMDXComponents()} />
      </DocsBody>
    </DocsPage>
  );
}
9
Replace Nextra’s search with Fumadocs search:
app/api/search/route.ts
import { source } from '@/lib/source';
import { createFromSource } from 'fumadocs-core/search/server';

export const { GET } = createFromSource(source);
No additional configuration needed - search works out of the box!

From Docusaurus

Migrating from Docusaurus requires more changes due to different architectures:
1

Create Next.js App

Start with a new Next.js project:
npx create-next-app@latest my-docs
cd my-docs
npm install fumadocs-ui fumadocs-core fumadocs-mdx
2

Copy Content

Copy your Docusaurus content:
cp -r ../old-docs/docs content/docs
cp -r ../old-docs/blog content/blog
3

Convert Frontmatter

Update Docusaurus frontmatter:
---
id: intro
sidebar_label: Introduction
sidebar_position: 1
title: Introduction
description: Get started with our platform
icon: Book
---
4

Convert Admonitions

Replace Docusaurus admonitions with Callouts:
:::note
This is a note
:::

<Callout type="info">
  This is a note
</Callout>
:::warning
Be careful!
:::

<Callout type="warn">
  Be careful!
</Callout>
5

Convert Code Blocks

Docusaurus code blocks work mostly the same:
```js title="config.js" showLineNumbers
module.exports = {
  title: 'My Site',
};

In Fumadocs:

```mdx
```js title="config.js"
module.exports = {
  title: 'My Site',
};

</Step>

<Step>

### Migrate sidebars.js

Convert `sidebars.js` to `meta.json` files:

```js title="docusaurus/sidebars.js"
module.exports = {
  docs: [
    'intro',
    {
      type: 'category',
      label: 'Guides',
      items: ['guides/installation', 'guides/configuration'],
    },
  ],
};
Becomes:
content/docs/meta.json
{
  "title": "Documentation",
  "pages": ["intro", "guides"]
}
content/docs/guides/meta.json
{
  "title": "Guides",
  "pages": ["installation", "configuration"]
}
6

Convert docusaurus.config.js

Translate Docusaurus config to Fumadocs:
docusaurus.config.js
module.exports = {
  title: 'My Site',
  url: 'https://mysite.com',
  themeConfig: {
    navbar: {
      title: 'My Site',
      items: [
        { to: '/docs/intro', label: 'Docs' },
        { to: '/blog', label: 'Blog' },
      ],
    },
  },
};
Becomes:
lib/layout.shared.tsx
import type { BaseLayoutProps } from 'fumadocs-ui/layouts/shared';

export function baseOptions(): BaseLayoutProps {
  return {
    nav: {
      title: 'My Site',
    },
    links: [
      { text: 'Docs', url: '/docs' },
      { text: 'Blog', url: '/blog' },
    ],
  };
}

From VitePress

1

Set Up Fumadocs

npx create-next-app@latest my-docs
cd my-docs
npm install fumadocs-ui fumadocs-core fumadocs-mdx
2

Copy Content

cp -r ../vitepress-docs/docs content/docs
3

Update Frontmatter

VitePress frontmatter is similar:
content/docs/index.mdx
---
title: Home
description: Welcome to our docs
layout: home
---
Remove VitePress-specific fields like layout.
4

Use VitePress Theme

Fumadocs includes a VitePress-inspired theme:
app/globals.css
@import 'tailwindcss';
@import 'fumadocs-ui/css/vitepress.css';
@import 'fumadocs-ui/css/preset.css';
5

Convert Custom Containers

Replace VitePress containers:
::: tip
This is a tip
:::

<Callout type="info">
  This is a tip
</Callout>

Upgrading Fumadocs Versions

From v14 to v15

Major changes in v15:
1

Update Dependencies

npm install fumadocs-ui@latest fumadocs-core@latest fumadocs-mdx@latest
2

Migrate to Tailwind CSS 4

Fumadocs v15+ requires Tailwind CSS 4:
npm install tailwindcss@next @tailwindcss/postcss@next
Update your CSS imports:
app/globals.css
@tailwind base;
@tailwind components;
@tailwind utilities;

@import 'tailwindcss';
@import 'fumadocs-ui/css/neutral.css';
@import 'fumadocs-ui/css/preset.css';
3

Update Color Variables

Color variables now use the @theme directive:
app/globals.css
:root {
  --background: hsl(0, 0%, 100%);
}

@theme {
  --color-fd-background: hsl(0, 0%, 100%);
  --color-fd-foreground: hsl(0, 0%, 3.9%);
  /* ... other colors */
}
4

Update Search Configuration

Search API moved to fumadocs-core/search/server:
app/api/search/route.ts
import { createSearchAPI } from 'fumadocs-core/search';
import { createFromSource } from 'fumadocs-core/search/server';

export const { GET } = createFromSource(source);

From v13 to v14

1

Update to Next.js 15

npm install next@latest react@latest react-dom@latest
2

Update Page Props

Next.js 15 made params and searchParams async:
app/docs/[[...slug]]/page.tsx
export default function Page(props: PageProps) {
  const page = source.getPage(props.params.slug);

export default async function Page(props: PageProps) {
  const params = await props.params;
  const page = source.getPage(params.slug);
3

Update Metadata Generation

export function generateMetadata(props: PageProps) {
  const page = source.getPage(props.params.slug);

export async function generateMetadata(props: PageProps) {
  const params = await props.params;
  const page = source.getPage(params.slug);

Breaking Changes Log

Refer to the changelog for detailed breaking changes:

v16.6.0

  • Search Results: Deprecated contentWithHighlights field. Highlights now use Markdown format (<mark>text</mark>)
  • Update search result rendering if using custom search UI

v16.5.0

  • Shiki Configuration: New universal Shiki configuration
  • Update code block configuration if customized

v16.0.0

  • Tailwind CSS 4: Required for all installations
  • Color System: Migrated to CSS @theme directive
  • Import Paths: Some internal imports changed

Content Migration Tools

Automated Frontmatter Conversion

Script to convert Nextra frontmatter:
scripts/convert-frontmatter.ts
import fs from 'fs';
import path from 'path';
import matter from 'gray-matter';

function convertFrontmatter(filePath: string) {
  const content = fs.readFileSync(filePath, 'utf-8');
  const { data, content: body } = matter(content);

  // Convert Nextra fields
  const converted = {
    title: data.title,
    description: data.description,
    // Remove Nextra-specific fields
  };

  const newContent = matter.stringify(body, converted);
  fs.writeFileSync(filePath, newContent);
}

// Run on all MDX files
const docsDir = 'content/docs';
const files = fs.readdirSync(docsDir, { recursive: true });

for (const file of files) {
  if (file.endsWith('.mdx')) {
    convertFrontmatter(path.join(docsDir, file));
  }
}

Batch Component Replacement

Replace old component syntax:
# Replace Nextra callouts with Fumadocs callouts
find content/docs -name "*.mdx" -exec sed -i 's/:::note/
<Callout type="info">/g' {} +
find content/docs -name "*.mdx" -exec sed -i 's/:::/
<\/Callout>/g' {} +

Checklist

Use this checklist to ensure complete migration:
  • Install Fumadocs packages
  • Create source.config.ts
  • Move content to content/docs/
  • Update frontmatter in all MDX files
  • Convert _meta.json to meta.json
  • Set up layouts (app/docs/layout.tsx)
  • Create page routes (app/docs/[[...slug]]/page.tsx)
  • Update component imports
  • Configure search API
  • Set up MDX components
  • Add Fumadocs CSS imports
  • Test all pages render correctly
  • Verify search functionality
  • Check dark mode works
  • Test responsive layouts
  • Update deployment configuration

Common Issues

Import Errors

If you get import errors:
Module not found: Can't resolve 'fumadocs-ui/mdx'
Ensure all Fumadocs packages are updated:
npm update fumadocs-ui fumadocs-core fumadocs-mdx

Build Failures

If builds fail with MDX errors:
  • Check that source.config.ts points to the correct directory
  • Verify all MDX files have valid frontmatter
  • Ensure fumadocs-mdx is in dependencies, not devDependencies

Styles Not Loading

If styles don’t apply:
  • Verify CSS imports in global.css
  • Check Tailwind CSS 4 is installed
  • Ensure className="flex flex-col min-h-screen" on body
  • Clear .next cache: rm -rf .next

Search Not Working

If search doesn’t work:
  • Verify search API route exists
  • Check that pages have structuredData
  • Ensure search client is configured
  • Test API endpoint directly: /api/search?query=test

Getting Help

If you encounter issues during migration:
  1. Check the official documentation
  2. Search GitHub issues
  3. Ask in GitHub Discussions
  4. Review example projects

Next Steps

After migrating:

Build docs developers (and LLMs) love