--- 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). :::
How are remark-math and rehype-katex different? 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 `...` 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.
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. :::