MDX extension for Diplodoc's markdown transformer that allows embedding MDX/JSX components within markdown content.
npm install @diplodoc/mdx-extension
# or
yarn add @diplodoc/mdx-extension
- Seamlessly integrate JSX/MDX components within markdown content
- Support for both client-side (CSR) and server-side (SSR) rendering
- Multiple syntax options:
- Explicit
<MDX>...</MDX>
tags - Short form JSX fragments
<>...</>
- Direct React component usage
<Component />
- Explicit
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()],
});
import React, {useMemo, useRef} from 'react';
import transform from '@diplodoc/transform';
import DefaultPlugins from '@diplodoc/transform/lib/plugins';
import {mdxPlugin, useMdx, isWithMdxArtifacts} 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()],
});
isWithMdxArtifacts(result);
return result;
}, []);
useMdx({
refCtr: ref,
html,
components: Components,
mdxArtifacts,
});
return <div ref={ref}></div>;
}
import React from 'react';
import transform from '@diplodoc/transform';
import DefaultPlugins from '@diplodoc/transform/lib/plugins';
import {mdxPlugin, useMdxSsr, getSsrRenderer} 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,
});
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);
useMdxSsr({
refCtr: ref,
components: Components,
mdxArtifacts,
html,
});
const innerHtml = useMemo(() => {
return {__html: html};
}, [html]);
return <div ref={ref} dangerouslySetInnerHTML={innerHtml}></div>;
}
The collect plugin provides functionality to process and transform MDX content while collecting artifacts. It comes in both synchronous and asynchronous versions.
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);
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);
The main plugin function that enables MDX processing.
render
: Optional renderer function, for SSR usegetSsrRenderer
tagNames?: string[]
- Optional array of tag names to filter which components will be processed
React hook for client-side MDX processing.
refCtr
: Ref to the container elementhtml
: HTML string from Diplodoc transformcomponents
: Object of React components to usemdxArtifacts
: MDX artifacts from transformpureComponents?
: Optional object of components that shouldn't hydrate (MDXComponents)
React hook for SSR-processed MDX content.
refCtr
: Ref to the container elementhtml
: HTML string from Diplodoc transformcomponents
: Object of React components to usemdxArtifacts
: MDX artifacts from transformpureComponents?
: Optional object of components that shouldn't hydrate (MDXComponents)
Creates an renderer function for client-side processing.
compileOptions?
: MDX compilation options (see MDX documentation)
Creates an SSR renderer function for server-side processing.
components
: Object of React components to usepureComponents?
: Optional object of components that shouldn't hydrate (MDXComponents)compileOptions?
: MDX compilation options (see MDX documentation)
Creates an asynchronous SSR renderer that supports withInitialProps
.
components
: Object of React components to usepureComponents?
: Optional object of components that shouldn't hydrate (MDXComponents)compileOptions?
: MDX compilation options (see MDX documentation)
Creates a synchronous collect plugin for processing MDX content.
tagNames?: string[]
- Optional array of tag names to filter processingpureComponents?
: Components that should skip client-side hydrationcompileOptions?
: MDX compilation options
Creates an asynchronous collect plugin that supports components with initial props.
tagNames?: string[]
- Optional array of tag names to filter processingpureComponents?
: Components that should skip client-side hydrationcompileOptions?
: MDX compilation options
Provides access to the current MDX state:
const state = useContext(MdxStateCtx);
Provides state setter function (only available during SSR):
const setState = useContext(MdxSetStateCtx);
// Usage in SSR:
setState?.({key: value});
Wraps a component to enable initial props fetching during SSR.
Parameters:
component
: React component to wrapgetInitProps
: Function that receives props and MDX state, returns props (sync or async)
<MDX>
<MyComponent prop="value" />
</MDX>
<>
<div>Fragment content</div>
</>
<Button onClick={() => console.log('click')}>
Click me
</Button>
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
- Serialized into the
-
MdxStateCtx
- A context that provides access to the current MDX state value
-
withInitialProps
- A higher-order component that enables asynchronous data fetching for SSR:- When wrapping a component with this function and using
getAsyncSsrRenderer
, thegetInitialProps
function will be called - Receives the component's props and MDX state as arguments
- Can return either static or promise-based props
- When wrapping a component with this function and using
-
getAsyncSsrRenderer
- An asynchronous version ofgetSsrRenderer
that:- Supports components wrapped with
withInitialProps
- Enables async data fetching during SSR
- Supports components wrapped with
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),
};
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,
};
All renderer functions (getSsrRenderer
, getAsyncSsrRenderer
, getRenderer
) now 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.
MIT