---
title: "MDX Runtime Compilation"
description: "MDX Runtime Compilation facilitates the parsing, frontmatter extraction, compilation, and evaluation of MDX documents on demand across server and browser runtimes. It leverages the MDX processor, U..."
last_updated: "2026-10-12T07:53:59.700425+00:00"
canonical_url: "https://www.doc0.dev/docs/e8872a72-1909-479c-b585-dc74c6e26726/technical/mdx-pipeline/mdx-runtime-compilation"
---

<details>
<summary>Relevant source files</summary>

The following files were used as context for generating this wiki page:

- [packages/mdx-remote/src/render.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx-remote/src/render.ts)
- [packages/mdx-remote/src/index.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx-remote/src/index.ts)
- [packages/local-md/src/eval-estree-expression/index.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/local-md/src/eval-estree-expression/index.ts)
- [packages/mdx-remote/src/client/index.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx-remote/src/client/index.ts)
- [packages/mdx-remote/src/compile.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx-remote/src/compile.ts)
- [packages/mdx/src/runtime/dynamic.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/runtime/dynamic.ts)
- [packages/mdx/src/runtime/browser.tsx](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/runtime/browser.tsx)
- [packages/core/src/content/md.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/content/md.ts)
- [packages/mdx/src/loaders/mdx/remark-include.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/loaders/mdx/remark-include.ts)
- [packages/local-md/src/md/compiler.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/local-md/src/md/compiler.ts)
- [packages/local-md/src/md/renderer.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/local-md/src/md/renderer.ts)
- [packages/mdx/src/webpack/mdx.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/webpack/mdx.ts)
- [packages/mdx/src/plugins/index-file.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/plugins/index-file.ts)
- [packages/mdx/src/loaders/mdx/index.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/loaders/mdx/index.ts)
- [packages/core/src/mdx-plugins/remark-llms.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/mdx-plugins/remark-llms.ts)
- [packages/local-md/src/js/executor-native.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/local-md/src/js/executor-native.ts)
- [packages/preview/src/lib/md.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/preview/src/lib/md.ts)
- [packages/preview/src/pages/...slugs.tsx](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/preview/src/pages/%5B...slugs%5D.tsx)
- [packages/radix-ui/src/mdx.tsx](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/radix-ui/src/mdx.tsx)
- [packages/mdx/src/loaders/mdx/build-mdx.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/loaders/mdx/build-mdx.ts)
- [packages/local-md/src/client.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/local-md/src/client.ts)
- [packages/mdx/src/next/index.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/next/index.ts)
- [packages/core/src/content/mdx/preset-runtime.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/content/mdx/preset-runtime.ts)
- [packages/mdx/src/runtime/server.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/runtime/server.ts)
- [packages/preview/src/components/markdown.tsx](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/preview/src/components/markdown.tsx)
- [packages/base-ui/src/mdx.tsx](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/base-ui/src/mdx.tsx)
- [packages/core/src/mdx-plugins/remark-mdx-files.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/mdx-plugins/remark-mdx-files.ts)
- [packages/base-ui/src/mdx.server.tsx](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/base-ui/src/mdx.server.tsx)
- [packages/core/src/mdx-plugins/remark-llms.runtime.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/mdx-plugins/remark-llms.runtime.ts)
- [packages/radix-ui/src/mdx.server.tsx](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/radix-ui/src/mdx.server.tsx)
</details>

## Overview

MDX Runtime Compilation facilitates the parsing, frontmatter extraction, compilation, and evaluation of MDX documents on demand across server and browser runtimes. It leverages the MDX processor, Unified transformation pipelines, and custom runtime execution functions to convert raw string inputs into executable React components, structured table-of-contents data, and metadata exports. By coordinating dynamic doc collections, standard schema validation, and custom component binding, the system enables flexible documentation rendering across diverse UI frameworks and bundlers.

Sources: [packages/mdx-remote/src/compile.ts:60-121](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx-remote/src/compile.ts#L60-L121), [packages/mdx-remote/src/render.ts:17-33](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx-remote/src/render.ts#L17-L33), [packages/mdx/src/runtime/dynamic.ts:47-122](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/runtime/dynamic.ts#L47-L122), [packages/mdx/src/runtime/browser.tsx:58-76](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/runtime/browser.tsx#L58-L76), [packages/mdx/src/runtime/server.ts:72-241](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/runtime/server.ts#L72-L241)

## Remote Compilation Architecture

### Compiler Factory and Processor Initialization

The `createCompiler` function initializes and caches a unified MDX processor instance configured via `createProcessor`. It accepts either a minimal configuration or a full preset configuration through `CompilerOptions`. If format options are omitted or set to `'detect'`, the processor defaults to MDX format.

```typescript
export function createCompiler(mdxOptions?: CompilerOptions) {
  let instance: ReturnType<typeof createProcessor> | undefined;

  function getProcessor() {
    if (instance) return instance;

    let format = mdxOptions?.format;
    if (!format || format === 'detect') format = 'mdx';

    return (instance = createProcessor({
      ...(mdxOptions?.preset === 'minimal' ? mdxOptions : getCompileOptions(mdxOptions)),
      format,
    }));
  }
```
Sources: [packages/mdx-remote/src/compile.ts:60-73](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx-remote/src/compile.ts#L60-L73)

The compilation pipeline processes raw source strings by first extracting frontmatter via `parseFrontmatter`. It then feeds the content body into the cached processor along with the file path and extracted frontmatter data.

```typescript
async compile<Frontmatter extends object = Record<string, unknown>>(
  options: CompileMDXOptions,
): Promise<CompileMDXResult<Frontmatter>> {
  const { scope = {}, skipRender } = options;
  const { frontmatter, content } = parseFrontmatter(options.source);

  const file = await this.compileFile({
    value: content,
    path: options.filePath,
    data: {
      frontmatter,
    },
  });
  const compiled = String(file);
```
Sources: [packages/mdx-remote/src/compile.ts:89-102](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx-remote/src/compile.ts#L60-L102)

> [!WARNING]
> Setting `skipRender` to `true` on `CompileMDXOptions` will bypass the execution phase, leaving `exports` as `null` and throwing an error if the resulting `body` function is invoked.

Sources: [packages/mdx-remote/src/compile.ts:44-48](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx-remote/src/compile.ts#L44-L48), [packages/mdx-remote/src/compile.ts:109-115](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx-remote/src/compile.ts#L109-L115)

### Execution Scope and Function Hydration

Once the MDX text is compiled into function body string output, `executeMdx` constructs a dynamic asynchronous function via `AsyncFunction`. It merges scope options, JSX runtimes, and base URLs into an `opts` object, exposing remaining scope properties as arguments to the generated function.

```typescript
export async function executeMdx(compiled: string, options: Options = {}) {
  const { opts: scopeOpts, ...scope } = options.scope ?? {};
  const fullScope = {
    opts: {
      ...(scopeOpts as object),
      ...(options.jsxRuntime ?? jsxRuntimeDefault),
      baseUrl: options.baseUrl,
    },
    ...scope,
  };

  const hydrateFn = new AsyncFunction(...Object.keys(fullScope), compiled);
  return (await hydrateFn.apply(hydrateFn, Object.values(fullScope))) as {
    default: MdxContent;
    toc?: TOCItemType[];
  };
}
```
Sources: [packages/mdx-remote/src/render.ts:17-33](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx-remote/src/render.ts#L17-L33)

| Option Key | Default / Source | Purpose |
| :--- | :--- | :--- |
| `scope` | `{}` | Custom variables and context injected into the MDX execution scope. |
| `baseUrl` | `pathToFileURL(filePath)` | Resolves relative file paths for remote compilation contexts. |
| `jsxRuntime` | `react/jsx-runtime` (or dev runtime) | Supplies JSX element creation functions during component hydration. |

Sources: [packages/mdx-remote/src/render.ts:8-12](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx-remote/src/render.ts#L8-L12), [packages/mdx-remote/src/compile.ts:76-81](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx-remote/src/compile.ts#L76-L81)

> [!NOTE]
> The synchronous counterpart `executeMdxSync` uses a standard synchronous `Function` constructor instead of `AsyncFunction`, evaluating compiled outputs immediately when asynchronous evaluation is not required.

Sources: [packages/mdx-remote/src/render.ts:35-52](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx-remote/src/render.ts#L35-L52)

## Client-Side Evaluation and Component Injection

### Asynchronous and Synchronous Hydration Functions

Client runtimes execute compiled MDX bundle strings by constructing dynamic functions via `executeMdx` and `executeMdxSync`. The `executeMdx` function uses an asynchronous function constructor derived from `executeMdx0` or `executeMdx`, while `executeMdxSync` utilizes a standard `Function` constructor to evaluate bundles synchronously.

```typescript
const AsyncFunction: new (...args: string[]) => (...args: unknown[]) => Promise<unknown> =
  Object.getPrototypeOf(executeMdx).constructor;

export async function executeMdx(compiled: string, options: Options = {}) {
  const { opts: scopeOpts, ...scope } = options.scope ?? {};
  const fullScope = {
    opts: {
      ...(scopeOpts as object),
      ...(options.jsxRuntime ?? jsxRuntimeDefault),
      baseUrl: options.baseUrl,
    },
    ...scope,
  };

  const hydrateFn = new AsyncFunction(...Object.keys(fullScope), compiled);
  return (await hydrateFn.apply(hydrateFn, Object.values(fullScope))) as {
    default: MdxContent;
    toc?: TOCItemType[];
  };
}
```
Sources: [packages/mdx-remote/src/render.ts:14-33](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx-remote/src/render.ts#L17-L33)

| Hydration Function | Constructor Type | Return Signature |
| :--- | :--- | :--- |
| `executeMdx` | `AsyncFunction` | `Promise<{ default: MdxContent; toc?: TOCItemType[] }>` |
| `executeMdxSync` | `Function` | `{ default: MdxContent; toc?: TOCItemType[] }` |

Sources: [packages/mdx-remote/src/render.ts:14-15](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx-remote/src/render.ts#L14-L15), [packages/mdx-remote/src/render.ts:28-32](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx-remote/src/render.ts#L28-L32), [packages/mdx-remote/src/render.ts:46-51](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx-remote/src/render.ts#L46-L51)

> [!NOTE]
> The legacy `executeMdx` implementation in `packages/mdx-remote/src/index.ts` automatically selects between `react/jsx-runtime` and `react/jsx-dev-runtime` based on `process.env.NODE_ENV === 'production'`.

Sources: [packages/mdx-remote/src/index.ts:10-17](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx-remote/src/index.ts#L10-L17)

### Scope Merging and Component Bindings

Both `executeMdx` and `executeMdxSync` accept an `Options` object containing scope variables, base URLs, and custom JSX runtimes. The options merge property values into an `opts` object, exposing remaining scope properties as arguments to the generated hydration function.

```typescript
export async function executeMdx(compiled: string, options: Options = {}) {
  const { opts: scopeOpts, ...scope } = options.scope ?? {};
  const fullScope = {
    opts: {
      ...(scopeOpts as object),
      ...(options.jsxRuntime ?? jsxRuntimeDefault),
      baseUrl: options.baseUrl,
    },
    ...scope,
  };
```
Sources: [packages/mdx-remote/src/render.ts:17-26](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx-remote/src/render.ts#L17-L26)

> [!WARNING]
> Failing to supply a valid `jsxRuntime` option defaults execution to `jsxRuntimeDefault` (`react/jsx-runtime`), which may cause runtime errors if development-only JSX annotations are required by the compiled bundle.

Sources: [packages/mdx-remote/src/render.ts:4-4](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx-remote/src/render.ts#L4-L4), [packages/mdx-remote/src/render.ts:22-22](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx-remote/src/render.ts#L22-L22), [packages/mdx-remote/src/render.ts:40-40](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx-remote/src/render.ts#L40-L40)

The resulting `MdxContent` component accepts optional `components` bindings matching `MDXComponents` alongside table-of-contents data structured as `TOCItemType[]`.

```typescript
import type { MDXComponents } from 'mdx/types';
import type { TOCItemType } from 'fumadocs-core/toc';
import type { FC } from 'react';
import jsxRuntimeDefault from 'react/jsx-runtime';

export type MdxContent = FC<{ components?: MDXComponents }>;
```
Sources: [packages/mdx-remote/src/render.ts:1-6](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx-remote/src/render.ts#L1-L6), [packages/mdx-remote/src/render.ts:29-32](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx-remote/src/render.ts#L29-L32)

## Dynamic Doc Collection Runtime

### On-Demand Collection Initialization and Execution

The runtime compilation engine initializes dynamic documentation collections using `dynamic` and `server` modules. The `dynamic` function initializes core configurations via `createCore` and builds configuration exports from the working directory using `buildConfig`.

```typescript
export async function dynamic<Config, TC extends InternalTypeConfig>(
  configExports: Config,
  coreOptions: CoreOptions,
  serverOptions?: ServerOptions,
) {
  const core = createCore(coreOptions);
  await core.init({
    config: buildConfig(configExports as Record<string, unknown>, process.cwd()),
  });
```
Sources: [packages/mdx/src/runtime/dynamic.ts:47-55](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/runtime/dynamic.ts#L47-L55)

> [!NOTE]
> The `getDocCollection` utility validates collection existence, retrieving either standard documentation arrays or single documentation entries based on the collection type definition.

Sources: [packages/mdx/src/runtime/dynamic.ts:59-65](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/runtime/dynamic.ts#L59-L65)

Lazy entry compilation reads raw file content via `node:fs/promises`, strips frontmatter metadata using the `frontmatter` utility, and compiles MDX through `buildMDX` in runtime environment mode. The compiled output string is then evaluated using an asynchronous function constructor initialized by `executeMdx`.

```typescript
async function compile({ info, data }: LazyEntry<unknown>) {
  let content = await fs.readFile(info.fullPath, 'utf-8');
  content = frontmatter(content).content;

  const compiled = await buildMDX(core, collection, {
    filePath: info.fullPath,
    source: content,
    frontmatter: data as Record<string, unknown>,
    isDevelopment: false,
    environment: 'runtime',
  });

  return (await executeMdx(String(compiled.value), {
    baseUrl: pathToFileURL(info.fullPath),
  })) as CompiledMDXProperties;
}
```
Sources: [packages/mdx/src/runtime/dynamic.ts:71-86](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/runtime/dynamic.ts#L71-L86)

| Execute Option | Type | Purpose |
| :--- | :--- | :--- |
| `scope` | `Record<string, unknown>` | Injected variables and options scope for MDX execution |
| `baseUrl` | `string \| URL` | File URL reference for relative asset resolution |
| `jsxRuntime` | `unknown` | Custom JSX runtime fallback overriding default imports |

Sources: [packages/mdx/src/runtime/dynamic.ts:22-26](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/runtime/dynamic.ts#L22-L26), [packages/mdx/src/runtime/dynamic.ts:33-39](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/runtime/dynamic.ts#L33-L39)

### Server Document Mapping and Lazy Collections

The `server` module provides utility factories including `fileInfo`, `mapDocData`, and `createDocMethods` to construct document metadata and method bindings for file entries. Passthrough properties defined in server options are explicitly mapped into document data records alongside body contents, table-of-contents trees, and structured data extracts.

```typescript
export function server<Config, TC extends InternalTypeConfig>(options: ServerOptions = {}) {
  const { doc: { passthroughs: docPassthroughs = [] } = {} } = options;

  function fileInfo(file: string, base: string): FileInfo {
    if (file.startsWith('./')) {
      file = file.slice(2);
    }

    return {
      path: file,
      fullPath: path.join(base, file),
    };
  }

  function mapDocData(entry: CompiledMDXProperties): DocData {
    const data: DocData = {
      body: entry.default,
      toc: entry.toc,
      structuredData: entry.structuredData,
      _exports: entry as unknown as Record<string, unknown>,
    };

    for (const key of docPassthroughs) {
      // @ts-expect-error -- handle passthrough properties
      data[key] = entry[key];
    }

    return data;
  }
```
Sources: [packages/mdx/src/runtime/server.ts:72-100](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/runtime/server.ts#L72-L100)

> [!WARNING]
> Calling `getText('processed')` or `getMDAST()` on document methods throws an error unless `includeProcessedMarkdown` or `includeMDAST` are enabled in the collection configuration.

Sources: [packages/mdx/src/runtime/server.ts:296-310](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/runtime/server.ts#L296-L310)

Collections support both eager and lazy loading variants through `doc`, `docLazy`, `docs`, `docsLazy`, and `meta` methods. Lazy execution caches compilation promises per entry, preventing redundant file reads and MDX build steps during subsequent document retrievals.

```typescript
  for (const entry of entries) {
    head[entry.info.path] = () => entry.data;
    let cachedResult: Promise<CompiledMDXProperties> | undefined;
    body[entry.info.path] = () => (cachedResult ??= compile(entry));
  }

  return { head, body };
}
```
Sources: [packages/mdx/src/runtime/dynamic.ts:88-95](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/runtime/dynamic.ts#L88-L95), [packages/mdx/src/runtime/server.ts:126-155](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/runtime/server.ts#L126-L155)

The `toFumadocsSource` function aggregates page and metadata items into a unified virtual file structure for downstream source consumers, applying optional base directory paths to all registered virtual file entries.

```typescript
export function toFumadocsSource<
  Page extends DocMethods & PageData,
  Meta extends MetaMethods & MetaData,
>(
  pages: Page[],
  metas: Meta[],
  options?: ToFumadocsSourceOptions,
): Source<{
  pageData: Page;
  metaData: Meta;
}> {
  const baseDir = options?.baseDir;
  const files: VirtualFile<{
    pageData: Page;
    metaData: Meta;
  }>[] = [];

  for (const entry of pages) {
    files.push({
      type: 'page',
      path: baseDir ? path.join(baseDir, entry.info.path) : entry.info.path,
      absolutePath: entry.info.fullPath,
      data: entry,
    });
  }

  for (const entry of metas) {
    files.push({
      type: 'meta',
      path: baseDir ? path.join(baseDir, entry.info.path) : entry.info.path,
      absolutePath: entry.info.fullPath,
      data: entry,
    });
  }

  return {
    files,
  };
}
```
Sources: [packages/mdx/src/runtime/server.ts:243-281](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/runtime/server.ts#L243-L281)

## Browser Execution and Client Loaders

### Client Loader Creation and Cache Sharing

The browser runtime exports the `browser` factory and `createClientLoader` utility to manage compiled MDX file entries and render components within React client environments. To prevent duplicate chunks generated by code-splitting frameworks such as Tanstack Start from fracturing state, `createClientLoader` accepts an optional `id` configuration option to share document preloads across multiple instances through a global `loaderStore` map.

```tsx
const loaderStore = new Map<
  string,
  {
    preloaded: Map<string, CompiledMDXProperties>;
  }
>();

export function createClientLoader<
  Doc = CompiledMDXProperties,
  Props extends object | undefined = undefined,
>(
  globEntries: Record<string, () => Promise<Doc>>,
  options: ClientLoaderOptions<Doc, Props>,
): ClientLoader<Doc, Props> {
  const { id = '', component: useRenderer } = options;
  const renderers: Record<string, FC<Props>> = {};
  const loaders = new Map<string, () => Promise<Doc>>();
  const store = loaderStore.get(id) ?? {
    preloaded: new Map(),
  };
  loaderStore.set(id, store);

```
Sources: [packages/mdx/src/runtime/browser.tsx:78-99](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/runtime/browser.tsx#L78-L99)

> [!NOTE]
> File path keys registered in glob entries are normalized by stripping leading `./` prefixes to ensure consistent cache lookups regardless of glob path syntax.

Sources: [packages/mdx/src/runtime/browser.tsx:100-102](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/runtime/browser.tsx#L100-L102)

### Standard Schema Integration and Renderer Methods

The `doc` method on the browser collection entry infers frontmatter output types via `@standard-schema/spec` while wrapping raw glob entries and forwarding options to `createClientLoader`. 

```tsx
export function browser<Config, TC extends InternalTypeConfig>() {
  return {
    doc<Name extends keyof Config & string>(
      _name: Name,
      glob: Record<string, () => Promise<unknown>>,
    ) {
      const out: DocCollectionEntry = {
        raw: glob as DocCollectionEntry['raw'],
        createClientLoader({ id = _name as string, ...options }) {
          return createClientLoader(this.raw, { id, ...options });
        },
      };

      return out as Config[Name] extends DocCollection<infer Schema> | DocsCollection<infer Schema>
        ? DocCollectionEntry<Name, StandardSchemaV1.InferOutput<Schema>, TC>
        : never;
    },
```
Sources: [packages/mdx/src/runtime/browser.tsx:58-74](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/runtime/browser.tsx#L58-L74)

Client loaders return an interface containing three core methods for retrieving and rendering documentation entries: `preload`, `getComponent`, and `useContent`.

| Method | Parameters | Return Type | Description |
| :--- | :--- | :--- | :--- |
| `preload` | `path: string` | `Promise<Doc>` | Asynchronously fetches and caches raw document content into the store |
| `getComponent` | `path: string` | `FC<Props>` | Returns a React functional component that renders the document using React's `use()` hook |
| `useContent` | `path: string, props?: Props` | `ReactNode` | Directly renders the document component populated with the provided properties |

Sources: [packages/mdx/src/runtime/browser.tsx:28-40](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/runtime/browser.tsx#L28-L40), [packages/mdx/src/runtime/browser.tsx:125-138](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/runtime/browser.tsx#L125-L138)

> [!WARNING]
> Requesting a path that does not exist in the available glob entries throws an error directly from `getLoader` during resolution.

Sources: [packages/mdx/src/runtime/browser.tsx:104-109](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/runtime/browser.tsx#L104-L109)

## Runtime Plugin Presets and Execution

### MDX Preset Configuration and Default Plugins

The `mdxPreset` function asynchronous helper builds and configures `ProcessorOptions` for unified and MDX pipelines by resolving default remark and rehype plugins alongside custom overrides. By default, the output format is set to `function-body`, and standard plugins can be disabled by passing `false` to their respective option flags in `MDXRuntimePresetOptions`.

| Preset Option | Default State | Associated Module / Plugin | Configuration Object |
| :--- | :--- | :--- | :--- |
| `remarkHeadingOptions` | Enabled (unless `false`) | `@/mdx-plugins/remark-heading` | `{ generateToc: false, ...remarkHeadingOptions }` |
| `remarkImageOptions` | Enabled (unless `false`) | `@/mdx-plugins/remark-image` | `remarkImageOptions` |
| `remarkCodeTabOptions` | Enabled (unless `false`) | `@/mdx-plugins/remark-code-tab` | `remarkCodeTabOptions` |
| `remarkNpmOptions` | Enabled (unless `false`) | `@/mdx-plugins/remark-npm` | `remarkNpmOptions` |
| `remarkStructureOptions` | Enabled (unless `false`) | `@/mdx-plugins/remark-structure` | `{ exportAs: 'structuredData', ...remarkStructureOptions }` |
| `rehypeCodeOptions` | Enabled (unless `false`) | `@/mdx-plugins/rehype-code` | `rehypeCodeOptions` |
| `remarkPlugins` / `rehypePlugins` | Resolves `v` | `remark-gfm` (remark), `@/mdx-plugins/rehype-toc` (rehype) | User-defined or resolved via `resolvePlugins` |

```typescript
export async function mdxPreset(options: MDXRuntimePresetOptions = {}): Promise<ProcessorOptions> {
  const {
    rehypeCodeOptions,
    remarkImageOptions,
    remarkHeadingOptions,
    remarkStructureOptions,
    remarkCodeTabOptions,
    remarkNpmOptions,
    outputFormat = 'function-body',
    ...mdxOptions
  } = options;

  const remarkPlugins = await resolvePlugins(
    (v) => [
      import('remark-gfm').then((mod) => mod.default),
      remarkHeadingOptions !== false &&
        import('@/mdx-plugins/remark-heading').then((mod) => [
          mod.remarkHeading,
          {
            generateToc: false,
            ...remarkHeadingOptions,
          },
        ]),
      remarkImageOptions !== false &&
        import('@/mdx-plugins/remark-image').then((mod) => [mod.remarkImage, remarkImageOptions]),
      remarkCodeTabOptions !== false &&
        import('@/mdx-plugins/remark-code-tab').then((mod) => [
          mod.remarkCodeTab,
          remarkCodeTabOptions,
        ]),
      remarkNpmOptions !== false &&
        import('@/mdx-plugins/remark-npm').then((mod) => [mod.remarkNpm, remarkNpmOptions]),
      ...v,
      remarkStructureOptions !== false &&
        import('@/mdx-plugins/remark-structure').then((mod) => [
          mod.remarkStructure,
          {
            exportAs: 'structuredData',
            ...remarkStructureOptions,
          } satisfies Plugins.StructureOptions,
        ]),
    ],
    mdxOptions.remarkPlugins,
  );

  const rehypePlugins = await resolvePlugins(
    (v) => [
      rehypeCodeOptions !== false &&
        import('@/mdx-plugins/rehype-code').then((mod) => [mod.rehypeCode, rehypeCodeOptions]),
      ...v,
      import('@/mdx-plugins/rehype-toc').then((mod) => mod.rehypeToc),
    ],
    mdxOptions.rehypePlugins,
  );

  return {
    ...mdxOptions,
    outputFormat,
    remarkPlugins,
    rehypePlugins,
  };
}
```
Sources: [packages/core/src/content/mdx/preset-runtime.ts:23-84](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/content/mdx/preset-runtime.ts#L23-L84)

> [!NOTE]
> The remark heading plugin explicitly forces `generateToc: false` by default within the preset runtime, separating table-of-contents generation into the rehype TOC pipeline.

Sources: [packages/core/src/content/mdx/preset-runtime.ts:38-45](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/content/mdx/preset-runtime.ts#L38-L45)

### File Tree Transformation and Auto-Files Parsing

The `remarkMdxFiles` transformer parses code blocks with the `files` language identifier or `auto-files` JSX flow elements into MDX component structures (`<Files />`, `<Folder />`, and `<File />`). The `parseFileTree` helper splits code lines, tracks indentation depth using standard Unix or Windows line endings, and builds hierarchical folder-file relationships. When encountering `<auto-files>` elements, the transformer extracts `dir`, `pattern`, and `defaultOpenAll` attributes, utilizing `tinyglobby` to scan directories relative to the virtual file's parent directory or CWD.

```typescript
function parseFileTree(code: string) {
  const lines = code.split(/\r?\n/);
  const stack = new Map<number, Node>();

  for (const line of lines) {
    let depth = 0;
    let name = line;
    let match: RegExpMatchArray | null;

    while ((match = /(?:├──|│|└──)\s*/.exec(name))) {
      name = name.slice(match[0].length);
      depth++;
    }

    if (!name) continue;
    const node: Node = name.endsWith('/')
      ? { type: 'folder', name, children: [], depth }
      : { type: 'file', name, depth };

    let parent: Node | undefined;
    for (let i = depth - 1; i >= 0 && !parent; i--) {
      parent = stack.get(i);
    }

    stack.set(depth, node);
    if (!parent) continue;
    if (parent.type === 'file') {
      Object.assign(parent, {
        type: 'folder',
        children: [],
      });
    }

    (parent as FolderNode).children.push(node);
  }

  return stack.get(0);
}

function defaultToMDX(node: Node, options: ToMdxOptions, depth = 0): MdxJsxFlowElement {
  if (depth === 0) {
    return {
      type: 'mdxJsxFlowElement',
      name: 'Files',
      attributes: [],
      children: [defaultToMDX(node, options, depth + 1)],
    };
  }

  const attributes: MdxJsxAttribute[] = [
    { type: 'mdxJsxAttribute', name: 'name', value: node.name },
  ];

  if (node.type === 'file') {
    return {
      type: 'mdxJsxFlowElement',
      attributes,
      children: [],
      name: 'File',
    };
  }

  if (options.defaultOpenAll) {
    attributes.push({
      type: 'mdxJsxAttribute',
      name: 'defaultOpen',
      value: null,
    });
  }

  return {
    type: 'mdxJsxFlowElement',
    attributes,
    name: 'Folder',
    children: node.children.map((item) => defaultToMDX(item, options, depth + 1)),
  };
}

/**
 *
```
Sources: [packages/core/src/mdx-plugins/remark-mdx-files.ts:40-162](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/mdx-plugins/remark-mdx-files.ts#L40-L162)

> [!WARNING]
> Omitting the `pattern` attribute on an `<auto-files>` element triggers an immediate failure on the virtual file (`file.fail`) at the node's exact AST position.

Sources: [packages/core/src/mdx-plugins/remark-mdx-files.ts:146-156](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/mdx-plugins/remark-mdx-files.ts#L146-L156)

### Placeholder Rendering and Serialization

The `renderPlaceholder` runtime function evaluates delimited control strings matching the control regex `/\0(.+?)\0/gs`, parsing inner JSON payloads into `PlaceholderData` objects containing `name`, `attributes`, and `children`. If nested child blocks contain text, `renderPlaceholder` executes recursively. If a matching renderer exists in the `renderers` dictionary, the placeholder output is replaced with the renderer's return value; otherwise, it falls back to the raw child text or the original match block upon a JSON parsing error.

```typescript
const regex = /\0(.+?)\0/gs;

export async function renderPlaceholder(
  text: string,
  renderers: Record<string, (data: PlaceholderData) => Awaitable<string>>,
): Promise<string> {
  let out = '';
  let idx = 0;

  for (const match of text.matchAll(regex)) {
    out += text.slice(idx, match.index);
    const inner = match[1];
    try {
      const data = JSON.parse(inner) as PlaceholderData;
      const renderer = data.name && renderers[data.name];
      if (data.children.trim()) {
        data.children = await renderPlaceholder(data.children, renderers);
      }

      if (renderer) {
        out += await renderer(data);
      } else {
        out += data.children;
      }
    } catch {
      out += match[0];
    }

    idx = match.index + match[0].length;
  }
  out += text.slice(idx);

  return out;
```
Sources: [packages/core/src/mdx-plugins/remark-llms.runtime.ts:10-42](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/mdx-plugins/remark-llms.runtime.ts#L10-L42)

## Preview and Dynamic Rendering Integration

### Markdown Compiler Setup and Plugin Pipelines

The markdown compiler pipeline is configured via `createMarkdownCompiler`, which accepts `remarkPlugins`, `rehypePlugins`, and `remarkRehypeOptions`. In preview environments, the processor integrates `remarkGfm`, `remarkMath`, `remarkMdxMermaid`, `remarkHeading`, `remarkNpm`, and `remarkCodeTab`, alongside `rehypeKatex`, `rehypeCode`, and `rehypeToc` with `{ exportToc: { as: 'data' } }`.

```tsx
const compiler = createMarkdownCompiler({
  remarkPlugins: [
    remarkGfm,
    remarkMath,
    remarkMdxMermaid,
    plugin(remarkHeading, { generateToc: false }),
    plugin(remarkNpm, { persist: { id: 'package-manager' } }),
    remarkCodeTab,
  ],
  remarkRehypeOptions: {
    passThrough: ['mdxJsxFlowElement', 'mdxJsxTextElement'],
  },
  rehypePlugins: [
    rehypeKatex,
    plugin(rehypeCode, { lazy: true, fallbackLanguage: 'text' }),
    plugin(rehypeToc, { exportToc: { as: 'data' } }),
  ],
});
```
Sources: [packages/preview/src/pages/...slugs.tsx:29-46](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/preview/src/pages/%5B...slugs%5D.tsx#L29-L46)

| Compiler Option | Configured Plugins / Options | Purpose |
| :--- | :--- | :--- |
| `remarkPlugins` | `remarkGfm`, `remarkMath`, `remarkMdxMermaid`, `remarkHeading`, `remarkNpm`, `remarkCodeTab` | Extends markdown parsing with tables, math, mermaid diagrams, headings, package managers, and code tabs. |
| `remarkRehypeOptions` | `passThrough: ['mdxJsxFlowElement', 'mdxJsxTextElement']` | Preserves JSX flow and text elements during AST conversion. |
| `rehypePlugins` | `rehypeKatex`, `rehypeCode`, `rehypeToc` | Applies KaTeX math formatting, code [syntax highlighting](https://www.doc0.dev/docs/e8872a72-1909-479c-b585-dc74c6e26726/technical/core-architecture/syntax-highlighting), and [table of contents](https://www.doc0.dev/docs/e8872a72-1909-479c-b585-dc74c6e26726/technical/ui-layouts/table-of-contents) extraction. |

Sources: [packages/preview/src/pages/...slugs.tsx:29-46](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/preview/src/pages/%5B...slugs%5D.tsx#L29-L46)

> [!CAUTION]
> The markdown compilation renderer throws an explicit error when encountering disallowed evaluation nodes, disallowing program evaluation and limiting identifier lookup strictly to override component bindings.

Sources: [packages/preview/src/lib/md.ts:45-55](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/preview/src/lib/md.ts#L45-L55)

### Component Injection and Rendering Handlers

The `DocPage` routing component loads layout and page structures based on configuration presets (`docs` or `flux`), resolving page slugs through source loaders. When rendering content inside `MdContent`, `ServerImage` intercepts non-absolute image sources and rewrites them into query parameters containing `page`, `project`, and `src`.

```tsx
function useMdxComponents(page: SourcePage) {
  function ServerImage({ src, ...rest }: ComponentProps<'img'>) {
    // resolve non-absolute src
    if (src && !URL.canParse(src)) {
      const params = new URLSearchParams();
      params.set('page', page.absolutePath!);
      params.set('project', page.data.project.dir);
      params.set('src', src);
      src = `/img?${params}`;
    }

    return <Image src={src} {...rest} />;
  }

  return {
    ...defaultMdxComponents,
    Mermaid,
    img: ServerImage,
  };
}
```
Sources: [packages/preview/src/pages/...slugs.tsx:48-67](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/preview/src/pages/%5B...slugs%5D.tsx#L48-L67)

> [!NOTE]
> When page compilation fails, the preview layout catches the error and formats the stack trace inside a code block component rather than crashing the request.

Sources: [packages/preview/src/pages/...slugs.tsx:170-194](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/preview/src/pages/%5B...slugs%5D.tsx#L170-L194)

## Related

- [MDX Loader Pipeline](https://www.doc0.dev/docs/e8872a72-1909-479c-b585-dc74c6e26726/technical/mdx-pipeline/mdx-loader-pipeline)


## Sitemap

See the full [sitemap](https://www.doc0.dev/docs/e8872a72-1909-479c-b585-dc74c6e26726/llms.txt) for all pages in this wiki.
