跳到主要内容

MDX 与 React

Docusaurus 内置了对 MDX v2的支持,这让你可以在 Markdown 文档中编写 JSX 并渲染为 React 组件。

Check out the MDX docs to see what fancy stuff you can do with MDX.

Debugging MDX

The MDX format is quite strict, and you may get compilation errors.

Use the MDX playground to debug them and make sure your syntax is valid.

Exporting components

To define any custom component within an MDX file, you have to export it: only paragraphs that start with export will be parsed as components instead of prose.

export const Highlight = ({children, color}) => (
<span
style={{
backgroundColor: color,
borderRadius: '2px',
color: '#fff',
padding: '0.2rem',
}}>
{children}
</span>
);

<Highlight color="#25c2a0">Docusaurus green</Highlight> and <Highlight color="#1877F2">Facebook blue</Highlight> are my favorite colors.

I can write **Markdown** alongside my _JSX_!

请注意,以下是 React 组件的标记和 Markdown 语法的渲染效果:

http://localhost:3000
Docusaurus green and Facebook blue are my favorite colors.

I can write Markdown alongside my JSX!

MDX is JSX

Since all doc files are parsed using MDX, anything that looks like HTML is actually JSX. Therefore, if you need to inline-style a component, follow JSX flavor and provide style objects.

/* Instead of this: */
<span style="background-color: red">Foo</span>
/* Use this: */
<span style={{backgroundColor: 'red'}}>Foo</span>

Importing components

You can also import your own components defined in other files or third-party components installed via npm.

<!-- Docusaurus theme component -->
import TOCInline from '@theme/TOCInline';
<!-- External component -->
import Button from '@mui/material/Button';
<!-- Custom component -->
import BrowserWindow from '@site/src/components/BrowserWindow';
提示

The @site alias points to your website's directory, usually where the docusaurus.config.js file is. Using an alias instead of relative paths ('../../src/components/BrowserWindow') saves you from updating import paths when moving files around, or when versioning docs and translating.

While declaring components within Markdown is very convenient for simple cases, it becomes hard to maintain because of limited editor support, risks of parsing errors, and low reusability. Use a separate .js file when your component involves complex JS logic:

src/components/Highlight.js
import React from 'react';

export default function Highlight({children, color}) {
return (
<span
style={{
backgroundColor: color,
borderRadius: '2px',
color: '#fff',
padding: '0.2rem',
}}>
{children}
</span>
);
}
markdown-file.mdx
import Highlight from '@site/src/components/Highlight';

<Highlight color="#25c2a0">Docusaurus green</Highlight>
提示

If you use the same component across a lot of files, you don't need to import it everywhere—consider adding it to the global scope. See below

MDX component scope

Apart from importing a component and exporting a component, a third way to use a component in MDX is to register it to the global scope, which will make it automatically available in every MDX file, without any import statements.

For example, given this MDX file:

- a
- list!

And some <Highlight>custom markup</Highlight>...

It will be compiled to a React component containing ul, li, p, and Highlight elements. Highlight is not a native html element: you need to provide your own React component implementation for it.

In Docusaurus, the MDX component scope is provided by the @theme/MDXComponents file. It's not a React component, per se, unlike most other exports under the @theme/ alias: it is a record from tag names like Highlight to their React component implementations.

If you swizzle this component, you will find all tags that have been implemented, and you can further customize our implementation by swizzling the respective sub-component, like @theme/MDXComponents/Code (which is used to render Markdown code blocks).

If you want to register extra tag names (like the <Highlight> tag above), you should consider wrapping @theme/MDXComponents, so you don't have to maintain all the existing mappings. Since the swizzle CLI doesn't allow wrapping non-component files yet, you should manually create the wrapper:

src/theme/MDXComponents.js
import React from 'react';
// Import the original mapper
import MDXComponents from '@theme-original/MDXComponents';
import Highlight from '@site/src/components/Highlight';

export default {
// Re-use the default mapping
...MDXComponents,
// Map the "<Highlight>" tag to our Highlight component
// `Highlight` will receive all props that were passed to `<Highlight>` in MDX
Highlight,
};

And now, you can freely use <Highlight> in every page, without writing the import statement:

I can conveniently use <Highlight color="#25c2a0">Docusaurus green</Highlight> everywhere!
http://localhost:3000

I can conveniently use Docusaurus green everywhere!

warning

We use upper-case tag names like Highlight on purpose.

From MDX v2+ onward (Docusaurus v3+), lower-case tag names are always rendered as native html elements, and will not use any component mapping you provide.

警告

This feature is powered by an MDXProvider. If you are importing Markdown in a React page, you have to supply this provider yourself through the MDXContent theme component.

src/pages/index.js
import React from 'react';
import FeatureDisplay from './_featureDisplay.mdx';
import MDXContent from '@theme/MDXContent';

export default function LandingPage() {
return (
<div>
<MDXContent>
<FeatureDisplay />
</MDXContent>
</div>
);
}

If you don't wrap your imported MDX with MDXContent, the global scope will not be available.

Markdown and JSX interoperability

Docusaurus v3 is using MDX v2.

The MDX syntax is mostly compatible with CommonMark, but is much stricter because your .mdx files are compiled into real React components (check the playground).

Some valid CommonMark features won't work with MDX (more info), notably:

  • Indented code blocks: use triple backticks instead
  • Autolinks (<http://localhost:3000>): use regular link syntax instead ([http://localhost:3000](http://localhost:3000))
  • HTML syntax (<p style="color: red;">): use JSX instead (<p style={{color: 'red'}}>)
  • Unescaped { and <: escape them with \ instead (\{ and \<)
提示

In case you need a less strict format, with CommonMark compatibility, you can use:

  • The .md file extension instead of .mdx
  • The format: md front matter

Importing code snippets

You can not only import a file containing a component definition, but also import any code file as raw text, and then insert it in a code block, thanks to Webpack raw-loader. In order to use raw-loader, you first need to install it in your project:

npm install --save raw-loader

现在就可以从文件中导入代码片段了:

myMarkdownFile.mdx
import CodeBlock from '@theme/CodeBlock';
import MyComponentSource from '!!raw-loader!./myComponent';

<CodeBlock language="jsx">{MyComponentSource}</CodeBlock>
http://localhost:3000
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

import React, {useState} from 'react';

export default function MyComponent() {
const [bool, setBool] = useState(false);
return (
<div>
<p>MyComponent rendered !</p>
<p>bool={bool ? 'true' : 'false'}</p>
<p>
<button onClick={() => setBool((b) => !b)}>toggle bool</button>
</p>
</div>
);
}

See using code blocks in JSX for more details of the <CodeBlock> component.

备注

You have to use <CodeBlock> rather than the Markdown triple-backtick ```, because the latter will ship out any of its content as-is, but you want to interpolate the imported text here.

warning

This feature is experimental and might be subject to breaking API changes in the future.

导入 Markdown

无论是在 Markdown 文件中还是在 React 页面中,你还可以将 Markdown 文件作为组件并将其导入到任何地方。

按照惯例,带有 _ 文件名前缀 的文件是不会创建任何文档页面的,这意味着可以将这类 Markdown 文件作为 "片段(partial)" 文件使用,以便将其导入到其它文件中。

_markdown-partial-example.mdx
<span>Hello {props.name}</span>

This is text some content from `_markdown-partial-example.mdx`.
someOtherDoc.mdx
import PartialExample from './_markdown-partial-example.mdx';

<PartialExample name="Sebastien" />
http://localhost:3000
Hello Sebastien

This is text some content from _markdown-partial-example.md.

This way, you can reuse content among multiple pages and avoid duplicating materials.

警告

Currently, the table of contents does not contain the imported Markdown headings. This is a technical limitation that we are trying to solve (issue).

Available exports

Within the MDX page, the following variables are available as globals:

  • frontMatter: the front matter as a record of string keys and values;
  • toc: the table of contents, as a tree of headings. See also Inline TOC for a more concrete use-case.
  • contentTitle: the Markdown title, which is the first h1 heading in the Markdown text. It's undefined if there isn't one (e.g. title specified in the front matter).
import TOCInline from '@theme/TOCInline';
import CodeBlock from '@theme/CodeBlock';

The table of contents for this page, serialized:

<CodeBlock className="language-json">{JSON.stringify(toc, null, 2)}</CodeBlock>

The front matter of this page:

<ul>
{Object.entries(frontMatter).map(([key, value]) => <li key={key}><b>{key}</b>: {value}</li>)}
</ul>

<p>The title of this page is: <b>{contentTitle}</b></p>
http://localhost:3000

The table of contents for this page, serialized:

[
{
"value": "Exporting components",
"id": "exporting-components",
"level": 3
},
{
"value": "Importing components",
"id": "importing-components",
"level": 3
},
{
"value": "MDX component scope",
"id": "mdx-component-scope",
"level": 3
},
{
"value": "Markdown and JSX interoperability",
"id": "markdown-and-jsx-interoperability",
"level": 3
},
{
"value": "Importing code snippets",
"id": "importing-code-snippets",
"level": 2
},
{
"value": "导入 Markdown",
"id": "importing-markdown",
"level": 2
},
{
"value": "Available exports",
"id": "available-exports",
"level": 2
}
]

The front matter of this page:

  • id: react
  • description: 借助 MDX,在 Docusaurus 管理的 Markdown 文档中可以使用 React 的功能
  • slug: /markdown-features/react

The title of this page is: MDX 与 React