Custom Source
Build your own content source
Introduction
Fumadocs is very flexible. You can integrate with any content source, even without an official adapter.
Examples
You can see examples to use Fumadocs with a CMS, which allows a nice experience on publishing content, and real-time update without re-building the app.
For a custom content source implementation, you will need:
Page Tree
You can either hardcode the page tree, or write some code to generate one. See Definitions of Page Tree.
Pass your page tree to DocsLayout
(usually in a layout.tsx
):
import { DocsLayout } from 'fumadocs-ui/layouts/docs';
import type { ReactNode } from 'react';
export default function Layout({ children }: { children: ReactNode }) {
return (
<DocsLayout
tree={
{
// your own tree
}
}
>
{children}
</DocsLayout>
);
}
The page tree is like a smarter "sidebar items", they will be referenced everywhere in the UI for navigation elements, such as the page footer.
Docs Page
For docs page, it's the same logic:
- Define path params (
slugs
). - Fetch page content from path params.
- Render the content.
Body
In the main body of page, find the corresponding page according to the slug and render its content inside the DocsPage
component.
You also need table of contents, which can be generated with your own implementation, or using the getTableOfContents
utility (Markdown/MDX only).
import { DocsPage, DocsBody } from 'fumadocs-ui/page';
import { getPage } from './my-content-source';
import { notFound } from 'next/navigation';
export default function Page({ params }: { params: { slug?: string[] } }) {
const page = getPage(params.slug);
if (!page) notFound();
return (
<DocsPage toc={page.tableOfContents}>
<DocsBody>{page.render()}</DocsBody>
</DocsPage>
);
}
Pre-rendering
The mechanism for pre-rendering differs depending on your React.js framework.
It's important to pre-render pages (especially the frequently visited ones) to improve initial load time.
In Next.js
Define the generateStaticParams
function to populate dynamic & catch-all routes.
When pre-rendering pages from a remote source, make sure to optimize repeated reads during the build phase. For example, if you're fetching content via GitHub API, it is better to clone the repository content locally for pre-rendering.
/docs/* -> GET github.com
/docs/introduction -> GET github.com
/docs/my-page -> GET github.com
Error: API Ratelimit
Document Search
This can be difficult considering your content may not be necessarily Markdown/MDX. For Markdown and MDX, the built-in Search API is adequate for most use cases. Otherwise, you will have to bring your own implementation.
We recommend 3rd party solutions like Orama or Algolia Search. They are more flexible than the built-in Search API, and is easier to integrate with remote sources. Fumadocs offers a simple Algolia Search Adapter, which includes a search client to integrate with Fumadocs UI.
MDX Remote
Fumadocs offers the MDX Remote package, it is a helper to integrate Markdown-based content sources with Fumadocs.
You can think it as a next-mdx-remote
with built-in plugins for Fumadocs.
Setup
npm install @fumadocs/mdx-remote
The main feature it offers is the MDX Compiler, it can compile MDX content to JSX nodes. Since it doesn't use a bundler, there's some limitations:
- No imports and exports in MDX files.
It's compatible with Server Components. For example:
import { createCompiler } from '@fumadocs/mdx-remote';
import { getPage } from './my-content-source';
import { DocsBody, DocsPage } from 'fumadocs-ui/page';
import { getMDXComponents } from '@/mdx-components';
const compiler = createCompiler({
// options
});
export default async function Page({
params,
}: {
params: { slug?: string[] };
}) {
const page = getPage(params.slug);
const compiled = await compiler.compile({
source: page.content,
});
const MdxContent = compiled.body;
return (
<DocsPage toc={compiled.toc}>
<DocsBody>
<MdxContent components={getMDXComponents()} />
</DocsBody>
</DocsPage>
);
}
Images
On serverless platforms like Vercel, the original public
folder (including static assets like images) will be removed after production build.
The MDX compiler might no longer be able to access local images in public
.
When referencing images, make sure to use a URL.
How is this guide?