Skip to main content

MDX Plugins

CodeHarborHub uses MDX to power its documentation. Sometimes, you may want to extend or tweak Markdown syntax. For example:

  • Embed a YouTube video using image-like syntax: ![](https://youtu.be/example)
  • Style standalone links as social cards
  • Automatically prepend a copyright notice to every page

The answer is: create an MDX plugin!
MDX supports a powerful plugin system to customize how Markdown is parsed and transformed into JSX.

There are three common plugin use-cases:

How MDX Plugins Work​

When Markdown is compiled, it goes through two intermediate steps:

  1. Markdown AST (MDAST)
  2. Hypertext AST (HAST)
  3. JSX Output

Plugins operate on these ASTs:

  • Remark → processes the Markdown AST
  • Rehype → processes the Hypertext AST
tip

Use plugins to introduce shorter syntax for frequently used JSX elements.
For example, CodeHarborHub’s admonition blocks are powered by a Remark plugin.

Default Plugins​

Docusaurus automatically injects a set of default Remark plugins during Markdown processing.
These handle tasks such as:

  • Generating a Table of Contents
  • Adding anchor links to each heading
  • Transforming images and links to require() calls

These built-in plugins are great examples to inspire your own custom plugins.

Installing Plugins​

An MDX plugin is usually an npm package, so install it like any other dependency.

Example: adding math support:

npm install --save remark-math@5 rehype-katex@6
Remark vs Rehype in action
  • remark-math extracts $...$ math expressions from Markdown and transforms them into a JSX placeholder.
  • rehype-katex then takes those placeholders and renders them into KaTeX-powered HTML.
warning

Many official Remark/Rehype plugins are ES Modules only. Docusaurus supports ESM, but we recommend using an ESM config file for easier imports.

Adding Plugins to docusaurus.config.js​

Once installed, import and add the plugin to your config:

docusaurus.config.js
import remarkMath from 'remark-math';
import rehypeKatex from 'rehype-katex';

export default {
presets: [
[
'@docusaurus/preset-classic',
{
docs: {
remarkPlugins: [remarkMath],
rehypePlugins: [rehypeKatex],
},
},
],
],
};

Using CommonJS? Dynamic imports make it work:

docusaurus.config.js
module.exports = async function createConfigAsync() {
return {
presets: [
[
'@docusaurus/preset-classic',
{
docs: {
remarkPlugins: [(await import('remark-math')).default],
rehypePlugins: [(await import('rehype-katex')).default],
},
},
],
],
};
};

Configuring Plugins​

Some plugins accept custom options. Use [plugin, options] syntax:

docusaurus.config.js
import rehypeKatex from 'rehype-katex';

export default {
presets: [
[
'@docusaurus/preset-classic',
{
docs: {
rehypePlugins: [
[rehypeKatex, {strict: false}],
],
},
},
],
],
};

Check the plugin’s documentation for available options.

Creating Your Own Plugin​

If no existing plugin fits your needs, you can create one. A plugin is a function that operates on the AST.

Example: prefix every h2 heading with Section X.

src/remark/section-prefix.js
import {visit} from 'unist-util-visit';

const plugin = () => {
return async (ast) => {
let count = 1;
visit(ast, 'heading', (node) => {
if (node.depth === 2 && node.children.length > 0) {
node.children.unshift({
type: 'text',
value: `Section ${count}. `,
});
count++;
}
});
};
};

export default plugin;

Import and register it:

docusaurus.config.js
import sectionPrefix from './src/remark/section-prefix';

export default {
presets: [
[
'@docusaurus/preset-classic',
{
docs: {
remarkPlugins: [sectionPrefix],
},
},
],
],
};
tip

The transformer function receives a second parameter, vfile, which provides metadata like the current Markdown file’s path.

Processing Order​

By default, Docusaurus runs its internal plugins before your custom plugins. If you need to run your plugin first:

docusaurus.config.js
export default {
presets: [
[
'@docusaurus/preset-classic',
{
docs: {
beforeDefaultRemarkPlugins: [sectionPrefix],
},
},
],
],
};

This ensures your transformations (like heading prefixes) are included in generated tables of contents.