---
id: plugins
description: Using MDX plugins to expand Docusaurus Markdown functionalities
slug: /markdown-features/plugins
---

# MDX Plugins

Sometimes, you may want to extend or tweak your Markdown syntax. For example:

- How do I embed youtube videos using the image syntax (`![](https://youtu.be/yKNxeF4KMsY)`)?
- How do I style links that are on their own lines differently, e.g., as a social card?
- How do I make every page start with a copyright notice?

And the answer is: create an MDX plugin! MDX has a built-in [plugin system](https://mdxjs.com/advanced/plugins/) that can be used to customize how the Markdown files will be parsed and transformed to JSX. There are three typical use-cases of MDX plugins:

- Using existing [remark plugins](https://github.com/remarkjs/remark/blob/main/doc/plugins.md#list-of-plugins) or [rehype plugins](https://github.com/rehypejs/rehype/blob/main/doc/plugins.md#list-of-plugins);
- Creating remark/rehype plugins to transform the elements generated by existing MDX syntax;
- Creating remark/rehype plugins to introduce new syntaxes to MDX.

If you play with the [MDX playground](https://mdxjs.com/playground/), you would notice that the MDX transpilation has two intermediate steps: Markdown AST (MDAST), and Hypertext AST (HAST), before arriving at the final JSX output. MDX plugins also come in two forms:

- **[Remark](https://github.com/remarkjs/remark/)**: processes the Markdown AST.
- **[Rehype](https://github.com/rehypejs/rehype/)**: processes the Hypertext AST.

:::tip

Use plugins to introduce shorter syntax for the most commonly used JSX elements in your project. The [admonition](./markdown-features-admonitions.mdx) syntax that we offer is also generated by a Remark plugin, and you could do the same for your own use case.

:::

## Default plugins {#default-plugins}

Docusaurus injects [some default Remark plugins](https://github.com/facebook/docusaurus/tree/main/packages/docusaurus-mdx-loader/src/remark) during Markdown processing. These plugins would:

- Generate the table of contents;
- Add anchor links to each heading;
- Transform images and links to `require()` calls.
- …

These are all typical use-cases of Remark plugins, which can also be a source of inspiration if you want to implement your own plugin.

## Installing plugins {#installing-plugins}

An MDX plugin is usually an npm package, so you install them like other npm packages using npm. Take the [math plugins](./markdown-features-math-equations.mdx) as an example.

```bash npm2yarn
npm install --save remark-math@5 rehype-katex@6
```

<details>
  <summary>How are <code>remark-math</code> and <code>rehype-katex</code> different?</summary>

In case you are wondering how Remark and Rehype are different, here is a good example. `remark-math` operates on the Markdown AST, where it sees text like `$...$`, and all it does is transform that to the JSX `<span class="math math-inline">...</span>` without doing too much with the content. This decouples the extraction of math formulae from their rendering, which means you can swap $\KaTeX$ out with other math renderers, like MathJax (with [`rehype-mathjax`](https://github.com/remarkjs/remark-math/tree/main/packages/rehype-mathjax)), just by replacing the Rehype plugin.

Next, the `rehype-katex` operates on the Hypertext AST where everything has been converted to HTML-like tags already. It traverses all the elements with `math` class and uses $\KaTeX$ to parse and render the content to actual HTML.

</details>

:::warning

Many official Remark/Rehype plugins are [**ES Modules only**](https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c), a JavaScript module system, which Docusaurus supports. We recommend using an [**ES Modules**](https://flaviocopes.com/es-modules/) config file to make it easier to import such packages.

:::

Next, import your plugins and add them to the plugin options through plugin or preset config in `docusaurus.config.js`:

```js title="docusaurus.config.js"
// highlight-start
import remarkMath from 'remark-math';
import rehypeKatex from 'rehype-katex';
// highlight-end

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

<details>
  <summary>Using a [**CommonJS**](https://nodejs.org/api/modules.html#modules-commonjs-modules) config file?</summary>

If you decide to use a CommonJS config file, it is possible to load those ES module plugins thanks to dynamic imports and an async config creator function:

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

</details>

## Configuring plugins {#configuring-plugins}

Some plugins can be configured and accept their own options. In that case, use the `[plugin, pluginOptions]` syntax, like this:

```js title="docusaurus.config.js"
import rehypeKatex from 'rehype-katex';

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

You should check your plugin's documentation for the options it supports.

## Creating new rehype/remark plugins {#creating-new-rehyperemark-plugins}

If there isn't an existing package that satisfies your customization need, you can create your own MDX plugin.

:::note

The writeup below is **not** meant to be a comprehensive guide to creating a plugin, but just an illustration of how to make it work with Docusaurus. Visit the [Remark](https://github.com/remarkjs/remark/blob/main/doc/plugins.md#create-plugins) or [Rehype](https://github.com/rehypejs/rehype/blob/main/doc/plugins.md#create-plugins) documentation for a more in-depth explanation of how they work.

:::

For example, let's make a plugin that visits every `h2` heading and adds a `Section X. ` prefix. First, create your plugin source file anywhere—you can even publish it as a separate npm package and install it like explained above. We would put ours at `src/remark/section-prefix.js`. A remark/rehype plugin is just a function that receives the `options` and returns a `transformer` that operates on the AST.

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

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

export default plugin;
```

You can now import your plugin in `docusaurus.config.js` and use it just like an installed plugin!

```js title="docusaurus.config.js"
// highlight-next-line
import sectionPrefix from './src/remark/section-prefix';

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

:::tip

The `transformer` has a second parameter [`vfile`](https://github.com/vfile/vfile) which is useful if you need to access the current Markdown file's path.

```js
const plugin = (options) => {
  const transformer = async (ast, vfile) => {
    ast.children.unshift({
      type: 'text',
      value: `The current file path is ${vfile.path}`,
    });
  };
  return transformer;
};
```

Our `transformImage` plugin uses this parameter, for example, to transform relative image references to `require()` calls.

:::

:::note

The default plugins of Docusaurus would operate before the custom remark plugins, and that means the images or links have been converted to JSX with `require()` calls already. For example, in the example above, the table of contents generated is still the same even when all `h2` headings are now prefixed by `Section X.`, because the TOC-generating plugin is called before our custom plugin. If you need to process the MDAST before the default plugins do, use the `beforeDefaultRemarkPlugins` and `beforeDefaultRehypePlugins`.

```js title="docusaurus.config.js"
export default {
  presets: [
    [
      '@docusaurus/preset-classic',
      {
        docs: {
          // highlight-next-line
          beforeDefaultRemarkPlugins: [sectionPrefix],
        },
      },
    ],
  ],
};
```

This would make the table of contents generated contain the `Section X.` prefix as well.

:::