@diplodoc/mdx-extension

中文文档
1.10.1发布于 2026/06/17TypeScriptESM + CJS近 30 天下载 6632

npm version License

原始使用文档
文档目录41 节
  1. Installation
  2. Features
  3. Usage
  4. Basic Setup
  5. Using Default Component Fallback
  6. Enabling MDX Input Validation
  7. Client-side Rendering (CSR)
  8. Server-side Rendering (SSR)
  9. Collect Plugin
  10. Synchronous Collect Plugin
  11. Asynchronous Collect Plugin
  12. Placeholder Renderer
  13. How it works
  14. Usage
  15. Async Placeholder Rendering
  16. Raw MDX Content Plugin
  17. How it works
  18. Usage
  19. Integration with Diplodoc Transform
  20. API Reference
  21. mdxPlugin(options?: { render?: MDXRenderer })
  22. useMdx(options: UseMdxProps): React.Fragment
  23. useMdxSsr(options: UseMdxSsrProps): React.Fragment
  24. getRenderer(options: GetRenderProps)
  25. getSsrRenderer(options: GetSsrRendererProps)
  26. getAsyncSsrRenderer(options: GetAsyncSsrRendererProps)
  27. getMdxCollectPlugin(options: Options)
  28. getAsyncMdxCollectPlugin(options: AsyncOptions)
  29. State Management Contexts
  30. Component Enhancers
  31. Syntax Examples
  32. Explicit MDX tags
  33. JSX Fragments
  34. Direct Component Usage
  35. Advanced Features
  36. State Management in SSR
  37. Asynchronous SSR with Initial Props
  38. Pure Components
  39. Asynchronous Component Loading
  40. Compilation Options
  41. License

@diplodoc/mdx-extension

npm version License

MDX extension for Diplodoc's markdown transformer that allows embedding MDX/JSX components within markdown content.

Installation

npm install @diplodoc/mdx-extension
# or
yarn add @diplodoc/mdx-extension

Features

  • Seamlessly integrate JSX/MDX components within markdown content
  • Support for both client-side (CSR) and server-side (SSR) rendering
  • Context support with tracking of context changes
  • Multiple syntax options:
    • Explicit <MDX>...</MDX> tags
    • Short form JSX fragments <>...</>
    • Direct React component usage <Component />
  • Built-in security with MDX input validation
  • Portal support for advanced component mounting with withPortal
  • Asynchronous component loading support via idMdxComponentLoader
  • Default component fallback for missing MDX references via recmaDefaultComponent

Usage

Basic Setup

First, add the mdxPlugin() to your Diplodoc transform plugins:

import transform from '@diplodoc/transform';
import DefaultPlugins from '@diplodoc/transform/lib/plugins';
import {mdxPlugin} from '@diplodoc/mdx-extension';

const result = transform(markdownContent, {
  plugins: [...DefaultPlugins, mdxPlugin()],
});

Using Default Component Fallback

The recmaDefaultComponent plugin provides graceful fallback for missing MDX component references. Instead of throwing errors, it redirects to a configurable default component.

import {mdxPlugin, recmaDefaultComponent} from '@diplodoc/mdx-extension';

const result = transform(markdownContent, {
  plugins: [
    ...DefaultPlugins,
    mdxPlugin({
      compileOptions: {
        recmaPlugins: [recmaDefaultComponent({componentName: 'DefaultComponent'})],
      },
    }),
  ],
});

Example default component:

const Components = {
  DefaultComponent: ({tagName, ...props}) => (
    <div style={{border: '1px dashed red'}}>Missing component: {tagName}</div>
  ),
};

Enabling MDX Input Validation

The validateMdx plugin is a recma plugin (Rehype Compatible MDAST to JavaScript AST compiler) that validates MDX content for security by analyzing the generated Abstract Syntax Tree (AST). It prevents execution of potentially unsafe code by restricting allowed JavaScript constructs and patterns.

To enable security validation of MDX input:

import {mdxPlugin, validateMdx} from '@diplodoc/mdx-extension';

const result = transform(markdownContent, {
  plugins: [
    ...DefaultPlugins,
    mdxPlugin({
      compileOptions: {
        recmaPlugins: [validateMdx],
      },
    }),
  ],
});

What the Plugin Validates

The validateMdx plugin performs comprehensive security validation by:

  1. Restricting JSX prop values to only allow:

    • Literals (strings, numbers, booleans)
    • Identifiers
    • Array expressions (with validated elements)
    • Object expressions (with validated properties)
    • Binary expressions (with validated operands)
    • Specific JSX calls (_jsxs, _jsx)
  2. Blocking dangerous props including:

    • dangerouslySetInnerHTML
    • ref
    • Event handlers (props starting with on[A-Z])
    • Unsafe URL protocols in href, src, action props (blocks javascript: and data:text/html)
  3. Limiting component types to only:

    • Built-in MDX components (_Fragment, MDXLayout, _createMdxContent)
    • Components from the _components object
    • Literal component names
  4. Disallowing unsafe JavaScript constructs:

    • Function declarations (except specific MDX functions)
    • Variable declarations (except specific patterns)
    • Member expressions (except specific patterns)
    • Call expressions (except specific functions)
    • New expressions (only Error allowed)
    • Spread elements (only specific patterns)
    • Logical expressions (only specific patterns)
    • Async/await, yield, assignment, import, and function expressions
  5. Enforcing depth limits (max depth 50) to prevent overly complex ASTs

Why Use This Plugin?

  • Security: Prevents execution of arbitrary JavaScript code in MDX content
  • Server-side protection: Blocks unsafe code that could execute during SSR
  • Controlled environment: Ensures only approved patterns are allowed
  • Early error detection: Throws validation errors for suspicious patterns before execution

Validation Errors

When the plugin detects invalid patterns, it throws descriptive errors such as:

  • "Component prop 'dangerouslySetInnerHTML' is not allowed"
  • "Component tag type '...' is not allowed here"
  • "FunctionDeclaration is not allowed here"
  • "Max depth exceeded"

Client-side Rendering (CSR)

import React, {Fragment, useMemo, useRef} from 'react';
import transform from '@diplodoc/transform';
import DefaultPlugins from '@diplodoc/transform/lib/plugins';
import {mdxPlugin, useMdx, isWithMdxArtifacts, validateMdx} from '@diplodoc/mdx-extension';

const Components = {
  CustomComponent: (props) => <div {...props}>Custom</div>,
};

const CONTENT = `
# Markdown Content

<CustomComponent style={{color: 'red'}} />

<MDX>
  <div>This will be rendered as MDX</div>
</MDX>
`;

function App() {
  const ref = useRef(null);

  const {html, mdxArtifacts} = useMemo(() => {
    const {result} = transform(CONTENT, {
      plugins: [
        ...DefaultPlugins,
        mdxPlugin({
          compileOptions: {
            recmaPlugins: [validateMdx],
          },
        }),
      ],
    });

    isWithMdxArtifacts(result);

    return result;
  }, []);

  const portals = useMdx({
    refCtr: ref,
    html,
    components: Components,
    mdxArtifacts,
  });

  return (
    <Fragment>
      <div ref={ref}></div>
      {portals}
    </Fragment>
  );
}

Server-side Rendering (SSR)

import React from 'react';
import transform from '@diplodoc/transform';
import DefaultPlugins from '@diplodoc/transform/lib/plugins';
import {mdxPlugin, useMdxSsr, getSsrRenderer, validateMdx} from '@diplodoc/mdx-extension';

const Components = {
  ServerComponent: (props) => <strong {...props}>Server Rendered</strong>,
};

const CONTENT = `
# Server Rendered Content

<ServerComponent />
`;

export async function getServerSideProps() {
  const render = await getSsrRenderer({
    components: Components,
    compileOptions: {
      recmaPlugins: [validateMdx],
    },
  });

  const {result} = transform(CONTENT, {
    plugins: [...DefaultPlugins, mdxPlugin({render})],
  });

  isWithMdxArtifacts(result);

  const {html, mdxArtifacts} = result;

  return {props: {html, mdxArtifacts}};
}

function ServerPage({html, mdxArtifacts}) {
  const ref = useRef(null);

  const portals = useMdxSsr({
    refCtr: ref,
    components: Components,
    mdxArtifacts,
    html,
  });

  const innerHtml = useMemo(() => {
    return {__html: html};
  }, [html]);

  return (
    <Fragment>
      <div ref={ref} dangerouslySetInnerHTML={innerHtml}></div>
      {portals}
    </Fragment>
  );
}

Collect Plugin

The collect plugin provides functionality to process and transform MDX content while collecting artifacts. It comes in both synchronous and asynchronous versions.

Synchronous Collect Plugin

import {getMdxCollectPlugin} from '@diplodoc/mdx-extension';

const plugin = getMdxCollectPlugin({
  tagNames: ['CustomComponent'], // Optional filter for specific tags
  pureComponents: PURE_COMPONENTS,
  compileOptions: {
    // MDX compilation options
  },
});

const transformedContent = plugin(originalContent);

Asynchronous Collect Plugin

import {getAsyncMdxCollectPlugin} from '@diplodoc/mdx-extension';

const asyncPlugin = getAsyncMdxCollectPlugin({
  tagNames: ['AsyncComponent'], // Optional filter for specific tags
  pureComponents: PURE_COMPONENTS,
  compileOptions: {
    // MDX compilation options
  },
});

const transformedContent = await asyncPlugin(originalContent);

Placeholder Renderer

The placeholder renderer provides a two-phase rendering approach for MDX content. This is useful for scenarios where you need to separate parsing from execution, such as deferred rendering, SSR with async components, or when you want to process MDX content in multiple passes.

How it works

  1. Placeholder Generation: During initial MDX processing, the getPlaceholderRenderer function creates placeholder HTML elements with unique IDs and stores the original MDX content in mdxArtifacts.idFragment.

  2. Placeholder Rendering: Later, the createPlaceholderRender or createAsyncPlaceholderRender functions process these placeholders, replacing them with actual rendered HTML and storing the compiled code in idMdx and idTagName for client-side hydration.

Usage

import {getPlaceholderRenderer, createPlaceholderRender} from '@diplodoc/mdx-extension';

// Create a placeholder renderer
const placeholderRenderer = getPlaceholderRenderer();

// Use it in mdxPlugin
const {result} = transform(content, {
  plugins: [
    ...DefaultPlugins,
    mdxPlugin({
      render: placeholderRenderer,
    }),
  ],
});

// Later, render the placeholders
const render = createPlaceholderRender({
  components: YOUR_COMPONENTS,
  // ... other options
});

const finalHtml = render(result.html, result.mdxArtifacts);

Async Placeholder Rendering

For asynchronous rendering (e.g., with components that have withInitialProps), use the async version:

import {createAsyncPlaceholderRender} from '@diplodoc/mdx-extension';

const asyncRender = createAsyncPlaceholderRender({
  components: YOUR_COMPONENTS,
  // ... other options
});

const finalHtml = await asyncRender(result.html, result.mdxArtifacts);

Raw MDX Content Plugin

The remarkRawMdxContent plugin is a remark plugin that extracts raw MDX content inside specified components and preserves it as text, preventing it from being parsed as MDX. This is useful for components that need to output raw MDX/JSX as text content (e.g., code examples, documentation generators).

How it works

The plugin processes MDX JSX elements and for specified tag names, extracts the raw text content between the opening and closing tags. It then replaces the children with a single text node containing that raw content, preserving the original formatting.

Usage

import {remarkRawMdxContent} from '@diplodoc/mdx-extension';
import {unified} from 'unified';
import remarkParse from 'remark-parse';
import remarkMdx from 'remark-mdx';

const processor = unified()
  .use(remarkParse)
  .use(remarkMdx)
  .use(remarkRawMdxContent, {
    tagNames: ['CodeExample', 'RawContent'], // Specify which tags to process
  });

const tree = processor.parse(`# Example

<CodeExample>
  <Button>Click me</Button>
</CodeExample>`);

const transformedTree = processor.runSync(tree);

Integration with Diplodoc Transform

You can use the plugin as part of your Diplodoc transform pipeline:

import transform from '@diplodoc/transform';
import DefaultPlugins from '@diplodoc/transform/lib/plugins';
import {mdxPlugin, remarkRawMdxContent} from '@diplodoc/mdx-extension';

const result = transform(content, {
  plugins: [
    ...DefaultPlugins,
    mdxPlugin({
      compileOptions: {
        remarkPlugins: [[remarkRawMdxContent, {tagNames: ['CodeExample']}]],
      },
    }),
  ],
});

This will ensure that content inside <CodeExample> tags is preserved as raw text rather than being parsed as MDX.

API Reference

mdxPlugin(options?: { render?: MDXRenderer })

The main plugin function that enables MDX processing.

Options:

  • render: Optional renderer function, for SSR use getSsrRenderer
  • tagNames?: string[] - Optional array of tag names to filter which components will be processed

useMdx(options: UseMdxProps): React.Fragment

React hook for client-side MDX processing.

Options:

  • refCtr: Ref to the container element
  • html: HTML string from Diplodoc transform
  • components: Object of React components to use
  • mdxArtifacts: MDX artifacts from transform
  • pureComponents?: Optional object of components that shouldn't hydrate (MDXComponents)
  • contextList?: Array of React contexts to provide to MDX components
  • idMdxComponentLoader?: Custom mdx component loader

useMdxSsr(options: UseMdxSsrProps): React.Fragment

React hook for SSR-processed MDX content.

Options:

  • refCtr: Ref to the container element
  • html: HTML string from Diplodoc transform
  • components: Object of React components to use
  • mdxArtifacts: MDX artifacts from transform
  • pureComponents?: Optional object of components that shouldn't hydrate (MDXComponents)
  • contextList?: Array of React contexts to provide to MDX components
  • idMdxComponentLoader?: Custom mdx component loader

getRenderer(options: GetRenderProps)

Creates an renderer function for client-side processing.

Options:

getSsrRenderer(options: GetSsrRendererProps)

Creates an SSR renderer function for server-side processing.

Options:

  • components: Object of React components to use
  • pureComponents?: Optional object of components that shouldn't hydrate (MDXComponents)
  • compileOptions?: MDX compilation options (see MDX documentation)
  • contextList?: Array of React contexts to provide to MDX components. Use { ctx, initValue } format to pass initial values for SSR

getAsyncSsrRenderer(options: GetAsyncSsrRendererProps)

Creates an asynchronous SSR renderer that supports withInitialProps.

Options:

  • components: Object of React components to use
  • pureComponents?: Optional object of components that shouldn't hydrate (MDXComponents)
  • compileOptions?: MDX compilation options (see MDX documentation)
  • contextList?: Array of React contexts to provide to MDX components. Use { ctx, initValue } format to pass initial values for SSR

getMdxCollectPlugin(options: Options)

Creates a synchronous collect plugin for processing MDX content.

Options:

  • tagNames?: string[] - Optional array of tag names to filter processing
  • pureComponents?: Components that should skip client-side hydration
  • compileOptions?: MDX compilation options

getAsyncMdxCollectPlugin(options: AsyncOptions)

Creates an asynchronous collect plugin that supports components with initial props.

Options:

  • tagNames?: string[] - Optional array of tag names to filter processing
  • pureComponents?: Components that should skip client-side hydration
  • compileOptions?: MDX compilation options

State Management Contexts

MdxStateCtx: Context<MdxStateCtxValue>

Provides access to the current MDX state:

const state = useContext(MdxStateCtx);

MdxSetStateCtx: Context<MdxSetStateCtxValue>

Provides state setter function (only available during SSR):

const setState = useContext(MdxSetStateCtx);
// Usage in SSR:
setState?.({key: value});

Component Enhancers

withInitialProps: WithInitialProps

Wraps a component to enable initial props fetching during SSR.

Parameters:

  • component: React component to wrap
  • getInitProps: Function that receives props and MDX state, returns props (sync or async)

withPortal: WithPortalProps

Wraps a component to render it through React.createPortal, allowing for more flexible mounting.

Parameters:

  • component: React component to wrap
  • fallback: Optional fallback component to show before portal is mounted

Usage:

export const COMPONENTS = {
  Tabs: withPortal(TabsLocal, () => <Skeleton />),
};

When using withPortal, the component will:

  1. Render the fallback component (if provided) initially
  2. Create a portal to mount the actual component when ready
  3. Clean up the portal when unmounted

Syntax Examples

Explicit MDX tags

<MDX>
  <MyComponent prop="value" />
</MDX>

JSX Fragments

<>

  <div>Fragment content</div>
</>

Direct Component Usage

<Button onClick={() => console.log('click')}>
Click me
</Button>

Advanced Features

State Management in SSR

The library provides two context providers for managing state during Server-Side Rendering (SSR):

  • MdxSetStateCtx - A context that provides a function to update the MDX state. This function is only available during SSR (null on client-side). If you set a component's state using this context, it will be:

    • Serialized into the data-mdx-state attribute during SSR
    • Available in MdxStateCtx when the component renders
  • MdxStateCtx - A context that provides access to the current MDX state value

Asynchronous SSR with Initial Props

  • withInitialProps - A higher-order component that enables asynchronous data fetching for SSR:

    • When wrapping a component with this function and using getAsyncSsrRenderer, the getInitialProps function will be called
    • Receives the component's props and MDX state as arguments
    • Can return either static or promise-based props
  • getAsyncSsrRenderer - An asynchronous version of getSsrRenderer that:

    • Supports components wrapped with withInitialProps
    • Enables async data fetching during SSR

Example usage:

const getInitialProps: MDXGetInitialProps<CounterProps> = (props, mdxState) => {
  mdxState.initialValue = 10; // Set initial state
  return props;
};

export const SSR_COMPONENTS = {
  ...COMPONENTS,
  Counter: withInitialProps(Counter, getInitialProps),
};

Pure Components

The library supports pure components that:

  • Are only rendered once during SSR
  • Skip hydration on the client side
  • Can be specified via the pureComponents option in:
    • useMdx
    • useMdxSsr
    • getSsrRenderer
    • getAsyncSsrRenderer

Example:

export const PURE_COMPONENTS = {
  KatexFormula, // Will render once on server and not hydrate
  Label, // on client
  CompatTable,
  Alert,
};

Asynchronous Component Loading

The useMdx and useMdxSsr hooks support an optional idMdxComponentLoader parameter that enables asynchronous loading of MDX components:

interface PageProps {
  html: string;
  mdxArtifacts?: MdxArtifacts;
  withLoader?: boolean;
}

const Page: FC<PageProps> = ({html, mdxArtifacts}) => {
  const [isSuccess, setSuccess] = React.useState(false);
  const [data, setData] = React.useState<IdMdxComponentLoader['data']>(undefined);

  useMdxSsr({
    // ...other options
    idMdxComponentLoader: {isSuccess, data},
  });

  useEffect(() => {
    (async () => {
      const idMdxComponent: Record<string, React.ComponentType<MDXProps>> = {};

      for (const [artifactId, code] of Object.entries(mdxArtifacts?.idMdx ?? {})) {
        const fn = await asyncExecuteCode(code);
        idMdxComponent[artifactId] = fn(runtime).default;
      }

      setData(idMdxComponent);
      setSuccess(true);
    })();
  }, [mdxArtifacts]);

  // ...rest of the component
};

Compilation Options

All renderer functions (getSsrRenderer, getAsyncSsrRenderer, getRenderer) accept optional MDX compilation options:

const renderer = await getAsyncSsrRenderer({
  components: SSR_COMPONENTS,
  pureComponents: PURE_COMPONENTS,
  compileOptions: {
    // MDX compilation options here
  },
});

This allows for fine-grained control over the MDX compilation process while maintaining the library's core functionality.

License

MIT

版本与发布记录

@diplodoc/mdx-extension 共发布 37 个版本,首次发布于 2025/05/26;过去 12 个月发布了 7 个版本。

版本发布日期距今
1.10.1latest2026/06/174 个月前
1.10.02026/06/174 个月前
1.9.02026/05/046 个月前
1.8.12026/04/306 个月前
1.8.02026/04/166 个月前
1.7.22026/04/146 个月前

关于 @diplodoc/mdx-extension 的常见问题

如何安装 @diplodoc/mdx-extension?

在项目目录运行 npm install @diplodoc/mdx-extension,或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖:react(^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0)、react-dom(^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0)。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖,Yarn 需要手动安装。

@diplodoc/mdx-extension 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@diplodoc/mdx-extension 支持 ESM 还是 CommonJS?

同时提供 ES Module 与 CommonJS 入口,import 和 require() 都可以使用,打包工具会自动选择合适的版本。

@diplodoc/mdx-extension 可以商用吗?

@diplodoc/mdx-extension 使用 未声明 许可。package.json 没有声明许可证。没有许可证的代码默认保留全部权利,商用前请联系作者或查看仓库中的 LICENSE 文件。以上为概括,不构成法律意见,请以许可原文为准。

@diplodoc/mdx-extension 的下载量有多少?

根据 npm 官方下载统计,@diplodoc/mdx-extension 在 2026/10/13 – 2026/11/11 这 30 天内被下载 6,632 次,日均约 221 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@diplodoc/mdx-extension 还在维护吗?

最新版本 1.10.1 发布于 2026/06/17(4 个月前);至今共发布 37 个版本,其中过去 12 个月发布了 7 个;首次发布于 2025/05/26。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。