mirror of
https://github.com/facebook/docusaurus.git
synced 2025-04-29 02:08:36 +02:00
* refactor: simplify imported code blocks - Use `language=<lang>` prop instead of "className="language-<lang>" for imported code blocks I've been using this in my personal projects ([example](https://raw.githubusercontent.com/nathan-contino-mongo/docusaurus-realm/9a2ebb6a43bbbd7b38899fcc86c903dcc1f61283/docs/sdk/kotlin/migrate-from-java-sdk.mdx)) since it is less verbose. If this interface is somehow less stable than the `className` prop, feel free to close this. But if both will work going forward we should probably recommend the simpler of the two. * Update markdown-features-react.mdx
465 lines
12 KiB
Text
465 lines
12 KiB
Text
---
|
|
id: react
|
|
title: MDX and React
|
|
description: Using the power of React in Docusaurus Markdown documents, thanks to MDX
|
|
slug: /markdown-features/react
|
|
---
|
|
|
|
# MDX and React
|
|
|
|
```mdx-code-block
|
|
import BrowserWindow from '@site/src/components/BrowserWindow';
|
|
import Tabs from '@theme/Tabs';
|
|
import TabItem from '@theme/TabItem';
|
|
import styles from './markdown-features-react.module.css';
|
|
```
|
|
|
|
## Using JSX in Markdown {#using-jsx-in-markdown}
|
|
|
|
Docusaurus has built-in support for [MDX v1](https://mdxjs.com/), which allows you to write JSX within your Markdown files and render them as React components.
|
|
|
|
:::note
|
|
|
|
While Docusaurus parses both `.md` and `.mdx` files using MDX, some of the syntaxes are treated slightly differently by third-party tools. For the most accurate parsing and better editor support, we recommend using the `.mdx` extension for files containing MDX syntax.
|
|
|
|
:::
|
|
|
|
To define any custom component within an MDX file, you have to export it.
|
|
|
|
```jsx
|
|
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_!
|
|
```
|
|
|
|
Notice how it renders both the markup from your React component and the Markdown syntax:
|
|
|
|
```mdx-code-block
|
|
export const Highlight = ({children, color}) => (
|
|
<span
|
|
style={{
|
|
backgroundColor: color,
|
|
borderRadius: '2px',
|
|
color: '#fff',
|
|
padding: '0.2rem',
|
|
}}>
|
|
{children}
|
|
</span>
|
|
);
|
|
|
|
<BrowserWindow minHeight={240}>
|
|
|
|
<Highlight color="#25c2a0">Docusaurus green</Highlight>
|
|
{` `}and <Highlight color="#1877F2">Facebook blue</Highlight> are my favorite colors.
|
|
|
|
I can write **Markdown** alongside my _JSX_!
|
|
|
|
</BrowserWindow>
|
|
```
|
|
|
|
:::caution 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.
|
|
|
|
<!-- prettier-ignore -->
|
|
```jsx
|
|
/* Instead of this: */
|
|
<span style="background-color: red">Foo</span>
|
|
/* Use this: */
|
|
<span style={{backgroundColor: 'red'}}>Foo</span>
|
|
```
|
|
|
|
This behavior is different from Docusaurus 1. See also [Migrating from v1 to v2](../../migration/migration-manual.md#convert-style-attributes-to-style-objects-in-mdx).
|
|
|
|
In addition, MDX is not [100% compatible with CommonMark](https://github.com/facebook/docusaurus/issues/3018). Use the **[MDX playground](https://mdx-git-renovate-babel-monorepo-mdx.vercel.app/playground)** to ensure that your syntax is valid MDX.
|
|
|
|
:::
|
|
|
|
### Importing components {#importing-components}
|
|
|
|
You can also import your own components defined in other files or third-party components installed via npm.
|
|
|
|
<!-- prettier-ignore -->
|
|
```md
|
|
<!-- 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';
|
|
```
|
|
|
|
:::tip
|
|
|
|
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](../docs/versioning.md) and [translating](../../i18n/i18n-tutorial.md).
|
|
|
|
:::
|
|
|
|
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:
|
|
|
|
```jsx title="src/components/Highlight.js"
|
|
export default function Highlight({children, color}) {
|
|
return (
|
|
<span
|
|
style={{
|
|
backgroundColor: color,
|
|
borderRadius: '2px',
|
|
color: '#fff',
|
|
padding: '0.2rem',
|
|
}}>
|
|
{children}
|
|
</span>
|
|
);
|
|
}
|
|
```
|
|
|
|
```md title="markdown-file.mdx"
|
|
import Highlight from '@site/src/components/Highlight';
|
|
|
|
<Highlight color="#25c2a0">Docusaurus green</Highlight>
|
|
```
|
|
|
|
Check out the [MDX docs](https://mdxjs.com/) to see what other fancy stuff you can do with MDX.
|
|
|
|
### Markdown and JSX interoperability {#markdown-and-jsx-interoperability}
|
|
|
|
Docusaurus v2 is using MDX v1, which has a lot of known cases where the content fails to be correctly parsed as Markdown. Use the **[MDX playground](https://mdx-git-renovate-babel-monorepo-mdx.vercel.app/playground)** to ensure that your syntax is valid MDX.
|
|
|
|
````mdx-code-block
|
|
<details>
|
|
|
|
<summary>Samples of parsing failures</summary>
|
|
|
|
**A paragraph starting with a JSX tag will be seen entirely as a JSX string:**
|
|
|
|
<Tabs groupId="jsx-and-md">
|
|
<TabItem value="Problem">
|
|
<div className={styles.wrappingBlock}>
|
|
|
|
```jsx
|
|
<span style={{color: 'red'}}>Highlighted text</span> but afterwards _Markdown_ **doesn't work**
|
|
```
|
|
|
|
</div>
|
|
<div className={styles.wrappingBlock}>
|
|
<BrowserWindow>
|
|
|
|
<span style={{color: 'red'}}>Highlighted text</span> but afterwards _Markdown_ **doesn't work**
|
|
|
|
</BrowserWindow>
|
|
</div>
|
|
</TabItem>
|
|
<TabItem value="Workaround">
|
|
|
|
Use JSX for the rest of the line, or prefix the line with some plain text:
|
|
|
|
<div className={styles.wrappingBlock}>
|
|
|
|
```jsx
|
|
<span style={{color: 'red'}}>Use JSX for the paragraph</span> to stop <i>worrying about</i> <b>Markdown</b>
|
|
|
|
​<span style={{color: 'red'}}>← This is a zero-width space</span> and afterwards <i>Markdown</i> <b>works</b>
|
|
```
|
|
|
|
</div>
|
|
<div className={styles.wrappingBlock}>
|
|
<BrowserWindow>
|
|
|
|
<span style={{color: 'red'}}>Use JSX for the paragraph</span> to stop <i>worrying about</i> <b>Markdown</b>
|
|
|
|
​<span style={{color: 'red'}}>← This is a zero-width space</span> and afterwards <i>Markdown</i> <b>works</b>
|
|
|
|
</BrowserWindow>
|
|
</div>
|
|
</TabItem>
|
|
</Tabs>
|
|
|
|
**Markdown within a JSX tag never works:**
|
|
|
|
<Tabs groupId="jsx-and-md">
|
|
<TabItem value="Problem">
|
|
<div className={styles.wrappingBlock}>
|
|
|
|
```jsx
|
|
<span style={{color: 'red'}}>**Bold doesn't work**</span>
|
|
```
|
|
|
|
</div>
|
|
<div className={styles.wrappingBlock}>
|
|
<BrowserWindow>
|
|
|
|
<span style={{color: 'red'}}>**Bold doesn't work**</span>
|
|
|
|
</BrowserWindow>
|
|
</div>
|
|
|
|
</TabItem>
|
|
<TabItem value="Workaround">
|
|
|
|
Use JSX within JSX tag, or move the Markdown to the outer layer:
|
|
|
|
<div className={styles.wrappingBlock}>
|
|
|
|
```jsx
|
|
<span style={{color: 'red'}}><b>Bold now works</b></span>
|
|
|
|
**<span style={{color: 'red'}}>Bold now works</span>**
|
|
```
|
|
|
|
</div>
|
|
<div className={styles.wrappingBlock}>
|
|
<BrowserWindow>
|
|
|
|
<span style={{color: 'red'}}><b>Bold now works</b></span>
|
|
|
|
**<span style={{color: 'red'}}>Bold now works</span>**
|
|
|
|
</BrowserWindow>
|
|
</div>
|
|
</TabItem>
|
|
</Tabs>
|
|
|
|
**Text immediately below a JSX tag will be seen as JSX text:**
|
|
|
|
<Tabs groupId="jsx-and-md">
|
|
<TabItem value="Problem">
|
|
<div className={styles.wrappingBlock}>
|
|
|
|
```jsx
|
|
<div style={{color: 'red'}}>
|
|
**Bold still doesn't work**
|
|
</div>
|
|
```
|
|
|
|
</div>
|
|
<div className={styles.wrappingBlock}>
|
|
<BrowserWindow>
|
|
|
|
<div style={{color: 'red'}}>
|
|
**Bold still doesn't work**
|
|
</div>
|
|
|
|
</BrowserWindow>
|
|
</div>
|
|
</TabItem>
|
|
<TabItem value="Workaround">
|
|
|
|
Add an empty new line:
|
|
|
|
<div className={styles.wrappingBlock}>
|
|
|
|
```jsx
|
|
<div style={{color: 'red'}}>
|
|
|
|
**Bold now works**
|
|
|
|
</div>
|
|
```
|
|
|
|
</div>
|
|
<div className={styles.wrappingBlock}>
|
|
<BrowserWindow>
|
|
<div style={{color: 'red'}}>
|
|
|
|
**Bold now works**
|
|
|
|
</div>
|
|
</BrowserWindow>
|
|
</div>
|
|
</TabItem>
|
|
</Tabs>
|
|
|
|
**Markdown text indented by four spaces will be seen as a code block:**
|
|
|
|
<Tabs groupId="jsx-and-md">
|
|
<TabItem value="Problem">
|
|
<div className={styles.wrappingBlock}>
|
|
|
|
```jsx
|
|
<div style={{color: 'red'}}>
|
|
|
|
You may think I'm just some text...
|
|
|
|
</div>
|
|
```
|
|
|
|
</div>
|
|
<div className={styles.wrappingBlock}>
|
|
<BrowserWindow>
|
|
|
|
<div style={{color: 'red'}}>
|
|
|
|
You may think I'm just some text...
|
|
|
|
</div>
|
|
|
|
</BrowserWindow>
|
|
</div>
|
|
</TabItem>
|
|
<TabItem value="Workaround">
|
|
|
|
Don't indent:
|
|
|
|
<div className={styles.wrappingBlock}>
|
|
|
|
```jsx
|
|
<div style={{color: 'red'}}>
|
|
|
|
Now I'm actually just text
|
|
|
|
</div>
|
|
```
|
|
|
|
</div>
|
|
<div className={styles.wrappingBlock}>
|
|
<BrowserWindow>
|
|
<div style={{color: 'red'}}>
|
|
|
|
Now I'm actually just text
|
|
|
|
</div>
|
|
</BrowserWindow>
|
|
</div>
|
|
</TabItem>
|
|
</Tabs>
|
|
</details>
|
|
````
|
|
|
|
## Importing code snippets {#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](https://webpack.js.org/loaders/raw-loader/). In order to use `raw-loader`, you first need to install it in your project:
|
|
|
|
```bash npm2yarn
|
|
npm install --save raw-loader
|
|
```
|
|
|
|
Now you can import code snippets from another file as it is:
|
|
|
|
<!-- prettier-ignore-start -->
|
|
```jsx title="myMarkdownFile.mdx"
|
|
import CodeBlock from '@theme/CodeBlock';
|
|
import MyComponentSource from '!!raw-loader!./myComponent';
|
|
|
|
<CodeBlock language="jsx">{MyComponentSource}</CodeBlock>
|
|
```
|
|
<!-- prettier-ignore-end -->
|
|
|
|
```mdx-code-block
|
|
import CodeBlock from '@theme/CodeBlock';
|
|
import MyComponentSource from '!!raw-loader!@site/src/pages/examples/_myComponent';
|
|
|
|
<BrowserWindow>
|
|
|
|
<CodeBlock language="jsx">{MyComponentSource}</CodeBlock>
|
|
|
|
</BrowserWindow>
|
|
|
|
<br />
|
|
```
|
|
|
|
See [using code blocks in JSX](./markdown-features-code-blocks.mdx#usage-in-jsx) for more details of the `<CodeBlock>` component.
|
|
|
|
:::note
|
|
|
|
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.
|
|
|
|
:::
|
|
|
|
## Importing Markdown {#importing-markdown}
|
|
|
|
You can use Markdown files as components and import them elsewhere, either in Markdown files or in React pages.
|
|
|
|
By convention, using the **`_` filename prefix** will not create any doc page and means the markdown file is a **"partial"**, to be imported by other files.
|
|
|
|
```md title="_markdown-partial-example.mdx"
|
|
<span>Hello {props.name}</span>
|
|
|
|
This is text some content from `_markdown-partial-example.mdx`.
|
|
```
|
|
|
|
```jsx title="someOtherDoc.mdx"
|
|
import PartialExample from './_markdown-partial-example.mdx';
|
|
|
|
<PartialExample name="Sebastien" />;
|
|
```
|
|
|
|
```mdx-code-block
|
|
import PartialExample from './_markdown-partial-example.mdx';
|
|
|
|
<BrowserWindow>
|
|
<PartialExample name="Sebastien" />
|
|
</BrowserWindow>
|
|
|
|
<br />
|
|
```
|
|
|
|
This way, you can reuse content among multiple pages and avoid duplicating materials.
|
|
|
|
:::caution
|
|
|
|
The table of contents does not currently contain the imported Markdown headings. This is a technical limitation that we are trying to solve ([issue](https://github.com/facebook/docusaurus/issues/3915)).
|
|
|
|
:::
|
|
|
|
## Available exports {#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](./markdown-features-inline-toc.mdx) 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).
|
|
|
|
```jsx
|
|
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>
|
|
```
|
|
|
|
```mdx-code-block
|
|
import TOCInline from '@theme/TOCInline';
|
|
|
|
<BrowserWindow>
|
|
|
|
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>
|
|
|
|
</BrowserWindow>
|
|
```
|