| id | plugins |
|---|---|
| description | Using MDX plugins to expand Markdown functionality in CodeHarborHub |
| slug | /markdown-features/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
When Markdown is compiled, it goes through two intermediate steps:
- Markdown AST (MDAST)
- Hypertext AST (HAST)
- JSX Output
Plugins operate on these ASTs:
:::tip
Use plugins to introduce shorter syntax for frequently used JSX elements.
For example, CodeHarborHub’s admonition blocks are powered by a Remark plugin.
:::
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.
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@6Remark vs Rehype in action
remark-mathextracts$...$math expressions from Markdown and transforms them into a JSX placeholder.rehype-katexthen 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. :::
Once installed, import and add the plugin to your config:
// highlight-start
import remarkMath from 'remark-math';
import rehypeKatex from 'rehype-katex';
// highlight-end
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],
},
},
],
],
};
};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.
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],
},
},
],
],
};:::tip
The transformer function receives a second parameter, vfile, which provides metadata like the current Markdown file’s path.
:::
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.