Skip to main content
doc0blade47/fumadocsdocumentation home
Sign in

MDX Runtime Compilation

Last updated Oct 12, 2026100% grounded
Relevant source files

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

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, packages/mdx-remote/src/render.ts:17-33, packages/mdx/src/runtime/dynamic.ts:47-122, packages/mdx/src/runtime/browser.tsx:58-76, packages/mdx/src/runtime/server.ts:72-241

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.

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

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.

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

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, packages/mdx-remote/src/compile.ts:109-115

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.

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

Option KeyDefault / SourcePurpose
scope{}Custom variables and context injected into the MDX execution scope.
baseUrlpathToFileURL(filePath)Resolves relative file paths for remote compilation contexts.
jsxRuntimereact/jsx-runtime (or dev runtime)Supplies JSX element creation functions during component hydration.

Sources: packages/mdx-remote/src/render.ts:8-12, packages/mdx-remote/src/compile.ts:76-81

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

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.

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

Hydration FunctionConstructor TypeReturn Signature
executeMdxAsyncFunctionPromise<{ default: MdxContent; toc?: TOCItemType[] }>
executeMdxSyncFunction{ default: MdxContent; toc?: TOCItemType[] }

Sources: packages/mdx-remote/src/render.ts:14-15, packages/mdx-remote/src/render.ts:28-32, packages/mdx-remote/src/render.ts:46-51

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

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.

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

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, packages/mdx-remote/src/render.ts:22-22, packages/mdx-remote/src/render.ts:40-40

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

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, packages/mdx-remote/src/render.ts:29-32

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.

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

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

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.

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

Execute OptionTypePurpose
scopeRecord<string, unknown>Injected variables and options scope for MDX execution
baseUrlstring | URLFile URL reference for relative asset resolution
jsxRuntimeunknownCustom JSX runtime fallback overriding default imports

Sources: packages/mdx/src/runtime/dynamic.ts:22-26, packages/mdx/src/runtime/dynamic.ts:33-39

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.

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

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

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.

  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, packages/mdx/src/runtime/server.ts:126-155

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.

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

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.

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

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

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.

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

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

MethodParametersReturn TypeDescription
preloadpath: stringPromise<Doc>Asynchronously fetches and caches raw document content into the store
getComponentpath: stringFC<Props>Returns a React functional component that renders the document using React's use() hook
useContentpath: string, props?: PropsReactNodeDirectly renders the document component populated with the provided properties

Sources: packages/mdx/src/runtime/browser.tsx:28-40, packages/mdx/src/runtime/browser.tsx:125-138

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

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 OptionDefault StateAssociated Module / PluginConfiguration Object
remarkHeadingOptionsEnabled (unless false)@/mdx-plugins/remark-heading{ generateToc: false, ...remarkHeadingOptions }
remarkImageOptionsEnabled (unless false)@/mdx-plugins/remark-imageremarkImageOptions
remarkCodeTabOptionsEnabled (unless false)@/mdx-plugins/remark-code-tabremarkCodeTabOptions
remarkNpmOptionsEnabled (unless false)@/mdx-plugins/remark-npmremarkNpmOptions
remarkStructureOptionsEnabled (unless false)@/mdx-plugins/remark-structure{ exportAs: 'structuredData', ...remarkStructureOptions }
rehypeCodeOptionsEnabled (unless false)@/mdx-plugins/rehype-coderehypeCodeOptions
remarkPlugins / rehypePluginsResolves vremark-gfm (remark), @/mdx-plugins/rehype-toc (rehype)User-defined or resolved via resolvePlugins
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

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

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.

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

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

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.

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

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' } }.

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

Compiler OptionConfigured Plugins / OptionsPurpose
remarkPluginsremarkGfm, remarkMath, remarkMdxMermaid, remarkHeading, remarkNpm, remarkCodeTabExtends markdown parsing with tables, math, mermaid diagrams, headings, package managers, and code tabs.
remarkRehypeOptionspassThrough: ['mdxJsxFlowElement', 'mdxJsxTextElement']Preserves JSX flow and text elements during AST conversion.
rehypePluginsrehypeKatex, rehypeCode, rehypeTocApplies KaTeX math formatting, code syntax highlighting, and table of contents extraction.

Sources: packages/preview/src/pages/...slugs.tsx:29-46

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

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.

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

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

Was this page helpful?