---
title: "MDX Bundling"
description: "MDX Bundling in Fumadocs is the bridge between static Markdown/MDX files and the framework-specific build pipelines (Vite, Next.js, Bun, Rolldown). It provides a unified system for transforming con..."
last_updated: "2026-07-02T09:46:39.644878+00:00"
canonical_url: "https://www.doc0.dev/docs/e8872a72-1909-479c-b585-dc74c6e26726/technical/content-engine/mdx-bundling"
---

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

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

- [packages/mdx/src/bun/index.ts](https://github.com/blade47/fumadocs/blob/main/packages/mdx/src/bun/index.ts)
- [packages/mdx/src/vite/index.ts](https://github.com/blade47/fumadocs/blob/main/packages/mdx/src/vite/index.ts)
- [packages/mdx/src/rolldown/index.ts](https://github.com/blade47/fumadocs/blob/main/packages/mdx/src/rolldown/index.ts)
- [packages/mdx/src/loaders/mdx/index.ts](https://github.com/blade47/fumadocs/blob/main/packages/mdx/src/loaders/mdx/index.ts)
- [packages/mdx/src/next/index.ts](https://github.com/blade47/fumadocs/blob/main/packages/mdx/src/next/index.ts)
- [packages/mdx/src/runtime/dynamic.ts](https://github.com/blade47/fumadocs/blob/main/packages/mdx/src/runtime/dynamic.ts)
- [packages/preview/src/pages/[...slugs].tsx](https://github.com/blade47/fumadocs/blob/main/packages/preview/src/pages/%5B...slugs%5D.tsx)
- [packages/mdx/src/plugins/index-file.ts](https://github.com/blade47/fumadocs/blob/main/packages/mdx/src/plugins/index-file.ts)
- [packages/mdx/package.json](https://github.com/blade47/fumadocs/blob/main/packages/mdx/package.json)
- [packages/mdx-remote/src/compile.ts](https://github.com/blade47/fumadocs/blob/main/packages/mdx-remote/src/compile.ts)
- [packages/content/src/index.ts](https://github.com/blade47/fumadocs/blob/main/packages/content/src/index.ts)
- [packages/mdx/src/loaders/mdx/build-mdx.ts](https://github.com/blade47/fumadocs/blob/main/packages/mdx/src/loaders/mdx/build-mdx.ts)
- [packages/mdx/src/loaders/mdx/remark-include.ts](https://github.com/blade47/fumadocs/blob/main/packages/mdx/src/loaders/mdx/remark-include.ts)
- [packages/mdx/src/runtime/server.ts](https://github.com/blade47/fumadocs/blob/main/packages/mdx/src/runtime/server.ts)
- [packages/mdx/src/loaders/mdx/remark-postprocess.ts](https://github.com/blade47/fumadocs/blob/main/packages/mdx/src/loaders/mdx/remark-postprocess.ts)
- [packages/core/src/content/mdx/preset-runtime.ts](https://github.com/blade47/fumadocs/blob/main/packages/core/src/content/mdx/preset-runtime.ts)
- [packages/local-md/src/md/compiler.ts](https://github.com/blade47/fumadocs/blob/main/packages/local-md/src/md/compiler.ts)
- [packages/content-collections/src/configuration.ts](https://github.com/blade47/fumadocs/blob/main/packages/content-collections/src/configuration.ts)
- [packages/mdx/src/config/preset.ts](https://github.com/blade47/fumadocs/blob/main/packages/mdx/src/config/preset.ts)
- [packages/mdx/tsdown.config.ts](https://github.com/blade47/fumadocs/blob/main/packages/mdx/tsdown.config.ts)
- [packages/core/src/content/mdx/preset-bundler.ts](https://github.com/blade47/fumadocs/blob/main/packages/core/src/content/mdx/preset-bundler.ts)
- [packages/content/src/runtime.ts](https://github.com/blade47/fumadocs/blob/main/packages/content/src/runtime.ts)
- [packages/mdx-remote/tsdown.config.ts](https://github.com/blade47/fumadocs/blob/main/packages/mdx-remote/tsdown.config.ts)
- [packages/base-ui/src/mdx.server.tsx](https://github.com/blade47/fumadocs/blob/main/packages/base-ui/src/mdx.server.tsx)
- [packages/vite/tsdown.config.ts](https://github.com/blade47/fumadocs/blob/main/packages/vite/tsdown.config.ts)
- [packages/mdx/src/node/loader.ts](https://github.com/blade47/fumadocs/blob/main/packages/mdx/src/node/loader.ts)
- [packages/mdx-remote/src/index.ts](https://github.com/blade47/fumadocs/blob/main/packages/mdx-remote/src/index.ts)
- [packages/mdx-remote/src/render.ts](https://github.com/blade47/fumadocs/blob/main/packages/mdx-remote/src/render.ts)
- [packages/mdx-remote/package.json](https://github.com/blade47/fumadocs/blob/main/packages/mdx-remote/package.json)
- [packages/openapi/src/utils/document/load.ts](https://github.com/blade47/fumadocs/blob/main/packages/openapi/src/utils/document/load.ts)
</details>

MDX Bundling in Fumadocs is the bridge between static Markdown/MDX files and the framework-specific build pipelines (Vite, Next.js, Bun, Rolldown). It provides a unified system for transforming content, ensuring consistent parsing, plugin execution, and asset handling across different environments. By decoupling the core MDX logic from the build tool integration, it enables specialized loaders and plugins to share the same processing pipeline while respecting environment-specific requirements.

The system is architected around a central `Core` engine that manages configurations and plugins. Build-tool-specific entry points (like `createMdxPlugin` for Bun or `createMDX` for Next.js) initialize this core and register loaders for MDX and metadata files. This design ensures that regardless of the underlying bundler, the content is consistently compiled, frontmatter is transformed, and output files are emitted according to the project’s configuration.

Key responsibilities of the bundling system include resolving content files via globbing, managing the MDX compilation lifecycle (including cache hash generation and post-processing), and generating virtual entry files that provide dynamic, type-safe access to content collections. This approach simplifies development, enabling features like hot reloading for configuration files and efficient, lazy-loaded content distribution.

## Core Bundling Architecture

The bundling system relies on `createCore` as the central initialization primitive. This core maintains the global state for MDX processing, including build configurations and collection metadata. Build-tool-specific adapters bridge this core into the target environment (e.g., Vite plugins, Webpack loaders).

```mermaid
flowchart TD
    A["Initialize (Core)"] --> B["Build Config (buildConfig)"]
    B --> C["Plugin Context (getPluginContext)"]
    C --> D["Register Loaders (MDX/Meta)"]
    D --> E["Execute Build (emit)"]
```
Sources: [packages/mdx/src/core.ts](https://github.com/blade47/fumadocs/blob/main/packages/mdx/src/core.ts#L1-L1), [packages/mdx/src/vite/index.ts](https://github.com/blade47/fumadocs/blob/main/packages/mdx/src/vite/index.ts#L60-L68)

## MDX Loading Pipeline

The MDX loader is the primary mechanism for transforming `.mdx` files. It manages cache generation, frontmatter parsing, and the invocation of the build processor.

1.  **Request Parsing:** The loader receives the source file and parses queries (e.g., `only=frontmatter`).
2.  **Frontmatter Processing:** It extracts frontmatter and applies collection-level transformations using the core configuration.
3.  **Compilation:** If full compilation is required, it invokes `buildMDX`, which uses an MDX processor configured with the environment-specific plugins.
4.  **Caching:** If experimental build caching is enabled, the loader computes a hash of the content to retrieve or store compiled results, reducing redundant compilation time.

Sources: [packages/mdx/src/loaders/mdx/index.ts](https://github.com/blade47/fumadocs/blob/main/packages/mdx/src/loaders/mdx/index.ts#L25-L109)

## Processor Configuration

The `buildMDX` function is the gatekeeper for processor instantiation. It utilizes a cache to store processor instances by collection and format (`md` vs `mdx`) to optimize repeated builds.

```typescript
function getProcessor(format: 'md' | 'mdx') {
  const cache = core.cache as Map<string, Processor>;
  const key = `build-mdx:${collection?.name ?? 'global'}:${format}`;
  let processor = cache.get(key);
  // ...
  processor = createProcessor({
    outputFormat: 'program',
    development: isDevelopment,
    ...mdxOptions,
    remarkPlugins: [
      remarkInclude,
      ...(mdxOptions.remarkPlugins ?? []),
      [remarkPostprocess, postprocessOptions],
    ],
    format,
  });
  cache.set(key, processor);
  return processor;
}
```
Sources: [packages/mdx/src/loaders/mdx/build-mdx.ts](https://github.com/blade47/fumadocs/blob/main/packages/mdx/src/loaders/mdx/build-mdx.ts#L81-L107)

## Index File Generation

The `index-file` plugin generates virtual entry points (e.g., `server.ts`, `dynamic.ts`) to provide runtime access to collections. These files contain code that invokes the server or dynamic compilation runtime to resolve and serve content efficiently.

- **Dynamic Collections:** If a collection is marked as `dynamic`, the generator uses glob patterns to identify files, extracts frontmatter, and creates lazy entry objects for runtime consumption.
- **Caching:** The index plugin utilizes a file system cache (`createFSCache`) to handle file updates efficiently, ensuring that index generation only triggers when necessary.

Sources: [packages/mdx/src/plugins/index-file.ts](https://github.com/blade47/fumadocs/blob/main/packages/mdx/src/plugins/index-file.ts#L51-L157)

## Remark Pipeline Customization

The system uses a preset-based approach to resolve plugins for both Remark and Rehype, allowing users to extend the pipeline without replacing the core logic. `resolvePlugins` and `pluginOption` are the primary mechanisms for merging default presets with user-defined overrides.

| Plugin | Responsibility |
| :--- | :--- |
| `remarkGfm` | GFM (GitHub Flavored Markdown) support |
| `remarkHeading` | TOC generation and heading IDs |
| `remarkStructure` | Exporting `structuredData` for search indexing |
| `rehypeCode` | Code block highlighting and processing |
| `rehypeToc` | TOC integration into the rendered MDX |

Sources: [packages/core/src/content/mdx/preset-bundler.ts](https://github.com/blade47/fumadocs/blob/main/packages/core/src/content/mdx/preset-bundler.ts#L38-L93), [packages/mdx/src/config/preset.ts](https://github.com/blade47/fumadocs/blob/main/packages/mdx/src/config/preset.ts#L68-L130)

## Runtime Execution

For dynamic content, the runtime (`dynamic.ts`) provides a mechanism to execute compiled MDX code directly. This is crucial for environments where build-time pre-compilation is insufficient.

> [!IMPORTANT]
> The runtime assumes the input code is safe and executes it directly via an `AsyncFunction` constructor. This is designed for content managed within the repository, not for arbitrary user input.

```typescript
async function executeMdx(compiled: string, options: ExecuteOptions = {}) {
  // ...
  const hydrateFn = new AsyncFunction(...Object.keys(fullScope), compiled);
  return await hydrateFn.apply(hydrateFn, Object.values(fullScope));
}
```
Sources: [packages/mdx/src/runtime/dynamic.ts](https://github.com/blade47/fumadocs/blob/main/packages/mdx/src/runtime/dynamic.ts#L32-L45)

## Design Trade-offs

| Design Choice | Benefit | Cost |
| :--- | :--- | :--- |
| **Virtual Index Files** | Type-safe content access, dynamic loading | Complexity in file generation and cache invalidation |
| **Processor Caching** | Faster build performance across re-runs | Increased memory usage for long-running processes |
| **Preset-based Plugins** | Easy extensibility, standardized behavior | Potential plugin conflicts if not resolved carefully |
| **Async Execution** | Enables runtime content compilation/lazy loading | Requires `AsyncFunction` overhead and assumes trusted content |

Sources: [packages/mdx/src/plugins/index-file.ts](https://github.com/blade47/fumadocs/blob/main/packages/mdx/src/plugins/index-file.ts#L49), [packages/mdx/src/loaders/mdx/build-mdx.ts](https://github.com/blade47/fumadocs/blob/main/packages/mdx/src/loaders/mdx/build-mdx.ts#L82), [packages/mdx/src/runtime/dynamic.ts](https://github.com/blade47/fumadocs/blob/main/packages/mdx/src/runtime/dynamic.ts#L31)

## Related

- [MDX Plugins](https://www.doc0.dev/docs/e8872a72-1909-479c-b585-dc74c6e26726/technical/content-engine/mdx-plugins)
- [Content Storage](https://www.doc0.dev/docs/e8872a72-1909-479c-b585-dc74c6e26726/technical/content-engine/content-storage)


## Sitemap

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