Skip to content

Commit

Permalink
docs: elaborate on the usage of MDX plugins (#5766)
Browse files Browse the repository at this point in the history
Co-authored-by: Joshua Chen <sidachen2003@gmail.com>
  • Loading branch information
cerkiewny and Josh-Cena authored Oct 29, 2021
1 parent fc32194 commit dc5a0b7
Showing 1 changed file with 151 additions and 31 deletions.
182 changes: 151 additions & 31 deletions website/docs/guides/markdown-features/markdown-features-plugins.mdx
Original file line number Diff line number Diff line change
@@ -1,72 +1,109 @@
---
id: plugins
title: Plugins
title: MDX Plugins
description: Using MDX plugins to expand Docusaurus Markdown functionalities
slug: /markdown-features/plugins
---

You can expand the MDX functionalities, using plugins.
Sometimes, you may want to extend or tweak your Markdown syntax. For example:

Docusaurus content plugins support both [Remark](https://github.com/remarkjs/remark) and [Rehype](https://github.com/rehypejs/rehype) plugins that work with MDX.
- How do I embed youtube videos using the image syntax (`![](https://youtu.be/yKNxeF4KMsY)`)?
- How do I style links that are on its own line differently, e.g., like a social card?
- How do I make every page start with a copyright notice?

## Configuring plugins {#configuring-plugins}
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 tranform 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](https://github.com/elviswolcott/remark-admonitions), and you could do the same for your own use-case.

:::

## Default plugins {#default-plugins}

An MDX plugin is usually a npm package, so you install them like other npm packages using npm.
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:

First, install your [Remark](https://github.com/remarkjs/remark/blob/main/doc/plugins.md#list-of-plugins) and [Rehype](https://github.com/rehypejs/rehype/blob/main/doc/plugins.md#list-of-plugins) plugins.
- Generate the table of contents;
- Add anchor links to each heading;
- Transform images and links to `require()` calls.
-

For example:
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 a npm package, so you install them like other npm packages using npm. Take the [math plugins](./markdown-features-math-equations.mdx) as example.

```bash npm2yarn
npm install --save remark-images
npm install --save rehype-truncate
npm install --save remark-math@3 rehype-katex@4
```

Next, import the plugins:
:::note

```js
const remarkImages = require('remark-images');
const rehypeTruncate = require('rehype-truncate');
```
There's recently a trend in the Remark/Rehype ecosystem to migrate to ES Modules, which Docusaurus doesn't support yet. Please make sure your installed plugin version is CommonJS-compatible before we officially support ESM.

:::

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

Finally, add them to the `@docusaurus/preset-classic` options in `docusaurus.config.js`:
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

```js {10,11} title="docusaurus.config.js"
module.exports = {
// ...
title: 'Docusaurus',
tagline: 'Build optimized websites quickly, focus on your content',
presets: [
[
'@docusaurus/preset-classic',
{
docs: {
sidebarPath: require.resolve('./sidebars.js'),
// ...
remarkPlugins: [remarkImages],
rehypePlugins: [rehypeTruncate],
// highlight-start
remarkPlugins: [math],
rehypePlugins: [katex],
// highlight-end
},
},
],
],
};
```

## Configuring plugin options {#configuring-plugin-options}
## Configuring plugins {#configuring-plugins}

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

```jsx {10-13} title="docusaurus.config.js"
```jsx title="docusaurus.config.js"
module.exports = {
// ...
presets: [
[
'@docusaurus/preset-classic',
{
docs: {
sidebarPath: require.resolve('./sidebars.js'),
// ...
remarkPlugins: [
plugin1,
[plugin2, {option1: {...}}],
remarkPlugins: [math],
rehypePlugins: [
// highlight-next-line
[katex, {strict: false}],
],
},
},
Expand All @@ -75,4 +112,87 @@ module.exports = {
};
```

See more information in the [MDX documentation](https://mdxjs.com/advanced/plugins).
You should check your plugin's documentation for options it supports.

## Creating new rehype/remark 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` which 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) {
if (node.children[0].type === 'text') {
node.children[0].value = `Section ${number}. ${node.children[0].value}`;
} else {
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!

```jsx 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],
},
},
],
],
};
```

:::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`.

```jsx 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.

:::

0 comments on commit dc5a0b7

Please sign in to comment.