diff --git a/website/docs/guides/markdown-features/markdown-features-plugins.mdx b/website/docs/guides/markdown-features/markdown-features-plugins.mdx index 90bbdee6d3e6..4cb8fa378382 100644 --- a/website/docs/guides/markdown-features/markdown-features-plugins.mdx +++ b/website/docs/guides/markdown-features/markdown-features-plugins.mdx @@ -1,48 +1,87 @@ --- 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. + +::: + +
+ 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 transforms 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. + +
-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 }, }, ], @@ -50,23 +89,21 @@ module.exports = { }; ``` -## 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}], ], }, }, @@ -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. + +:::