MDX Plugins
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:

- 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:
- Use existing Remark plugins or Rehype plugins
- Transform elements produced by MDX syntax
- Introduce new syntax to MDX
How MDX Plugins Work
When Markdown is compiled, it goes through two intermediate steps:
- Markdown AST (MDAST)
- Hypertext AST (HAST)
- JSX Output
Plugins operate on these ASTs:
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.
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:
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:
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:
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.
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:
import sectionPrefix from './src/remark/section-prefix';
export default {
presets: [
[
'@docusaurus/preset-classic',
{
docs: {
remarkPlugins: [sectionPrefix],
},
},
],
],
};
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:
export default {
presets: [
[
'@docusaurus/preset-classic',
{
docs: {
beforeDefaultRemarkPlugins: [sectionPrefix],
},
},
],
],
};
This ensures your transformations (like heading prefixes) are included in generated tables of contents.