---
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://mdx-git-renovate-babel-monorepo-mdx.vercel.app/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@3 rehype-katex@4
```

:::note

There's recently a trend in the Remark/Rehype ecosystem to migrate to ES Modules, a new JavaScript module system, which Docusaurus doesn't support yet. Please make sure your installed plugin version is CommonJS-compatible before we officially support ESM. Alternatively, you can read about using dynamic `import()` as a workaround in the tutorial of installing [`rehype-katex`](./markdown-features-math-equations.mdx#upgrading-rehype-katex-beyond-recommended-version).

:::

<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>

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

```js title="docusaurus.config.js"
// highlight-start
const math = require('remark-math');
const katex = require('rehype-katex');
// highlight-end

module.exports = {
  title: 'Docusaurus',
  tagline: 'Build optimized websites quickly, focus on your content',
  presets: [
    [
      '@docusaurus/preset-classic',
      {
        docs: {
          // highlight-start
          remarkPlugins: [math],
          rehypePlugins: [katex],
          // highlight-end
        },
      },
    ],
  ],
};
```

## 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"
module.exports = {
  presets: [
    [
      '@docusaurus/preset-classic',
      {
        docs: {
          remarkPlugins: [math],
          rehypePlugins: [
            // highlight-next-line
            [katex, {strict: false}],
          ],
        },
      },
    ],
  ],
};
```

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#creating-plugins) or [Rehype](https://github.com/remarkjs/remark/blob/main/doc/plugins.md#creating-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"
const visit = require('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;
};

module.exports = 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
const sectionPrefix = require('./src/remark/section-prefix');

module.exports = {
  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"
module.exports = {
  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.

:::