Skip to Content
Fumadocs

MDX Remote

Compiled Markdown/MDX content on demand.

Introduction

Fumadocs offers the MDX Remote package, a helper to integrate Markdown/MDX content sources with Fumadocs. Think of it as next-mdx-remote with built-in plugins for Fumadocs.

Do not pass untrusted MDX to the compiler, it runs as JavaScript in your app. Compile untrusted content as Markdown instead, it can't run JavaScript or raw HTML:

import { createCompiler } from '@fumadocs/mdx-remote';

const compiler = createCompiler({
  format: 'md',
  // skip reading files and fetching URLs for image sizes
  remarkImageOptions: false,
});

Setup

npm install @fumadocs/mdx-remote

Its main feature is the MDX compiler, which compiles MDX content to JSX nodes. It doesn't use a bundler, so there are some limitations:

  • Images have to be optimized at runtime.
  • No imports and exports in MDX files.

Usage

Create a compiler:

lib/compiler.ts
import { createCompiler } from '@fumadocs/mdx-remote';

export const compiler = createCompiler({
  // options
});

Compile the content of your pages, @/lib/source stands for your content source.

compileFile() compiles it to JavaScript in the loader, and executeMdxSync() runs it on the client:

src/routes/docs/$.tsx
import { createFileRoute } from '@tanstack/react-router';
import { createServerFn } from '@tanstack/react-start';
import { useMemo } from 'react';
import { executeMdxSync } from '@fumadocs/mdx-remote/client';
import { useFumadocsLoader } from 'fumadocs-core/source/client';
import { DocsLayout } from 'fumadocs-ui/layouts/docs';
import { DocsBody, DocsPage } from 'fumadocs-ui/layouts/docs/page';
import { compiler } from '@/lib/compiler';
import { getPage, source } from '@/lib/source';
import { getMDXComponents } from '@/components/mdx';

export const Route = createFileRoute('/docs/$')({
  component: Page,
  loader: ({ params }) => serverLoader({ data: params._splat?.split('/') ?? [] }),
});

const serverLoader = createServerFn({
  method: 'GET',
})
  .validator((slugs: string[]) => slugs)
  .handler(async ({ data: slugs }) => {
    const page = getPage(slugs);

    return {
      // compile to JavaScript, it runs on the client
      compiled: String(await compiler.compileFile(page.content)),
      pageTree: await source.serializePageTree(source.getPageTree()),
    };
  });

function Page() {
  const { compiled, pageTree } = useFumadocsLoader(Route.useLoaderData());
  const { default: MdxContent, toc } = useMemo(() => executeMdxSync(compiled), [compiled]);

  return (
    <DocsLayout tree={pageTree}>
      <DocsPage toc={toc}>
        <DocsBody>
          <MdxContent components={getMDXComponents()} />
        </DocsBody>
      </DocsPage>
    </DocsLayout>
  );
}

The frontmatter property returned by compile() is not validated. Validate it with a library like zod.

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?

Last updated on