id | description | slug |
---|---|---|
react |
Using the power of React in Docusaurus Markdown documents, thanks to MDX |
/markdown-features/react |
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';
Docusaurus has built-in support for MDX v1, 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.
:::
Check out the MDX docs to see what other fancy stuff you can do with MDX.
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_!
Notice how it renders both the markup from your React component and the Markdown syntax:
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.
/* 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.
In addition, MDX is not 100% compatible with CommonMark. Use the MDX playground to ensure that your syntax is valid MDX.
:::
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';
:::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 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:
import React from 'react';
export default function Highlight({children, color}) {
return (
<span
style={{
backgroundColor: color,
borderRadius: '2px',
color: '#fff',
padding: '0.2rem',
}}>
{children}
</span>
);
}
import Highlight from '@site/src/components/Highlight';
<Highlight color="#25c2a0">Docusaurus green</Highlight>
:::tip
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
:::
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
tags. Now, you can optionally provide your own implementation for any of these tags in the form of React components. (highlight
isn't even an intrinsic element: it needs an implementation!)
In Docusaurus, this MDX component scope is provided by the @theme/MDXComponents
component. It's not a React component, per se, unlike most other exports under the @theme/
alias: it is a record from tag names like ul
and img
to their custom implementations.
If you swizzle this component, you will find all tags that have been re-implemented, and you can further customize our implementation by swizzling the respective sub-component, like @theme/MDXComponents/Head
(which is used to implement the <head>
feature).
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:
import React from 'react';
// Import the original mapper
import MDXComponents from '@theme-original/MDXComponents';
// highlight-next-line
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-next-line
highlight: 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!
<BrowserWindow>
I can conveniently use <highlight color="#25c2a0">Docusaurus green</highlight> everywhere!
</BrowserWindow>
:::info
We use lower-case tag names like highlight
to "pretend" that they are intrinsic elements, but you can use capitalized ones like Highlight
as well.
:::
:::caution
This feature is powered by a wrapper provider. If you are importing Markdown in a React page, you have to supply this provider yourself through the MDXContent
theme component.
import React from 'react';
import FeatureDisplay from './_featureDisplay.mdx';
// highlight-next-line
import MDXContent from '@theme/MDXContent';
export default function LandingPage() {
return (
<div>
{/* highlight-start */}
<MDXContent>
<FeatureDisplay />
</MDXContent>
{/* highlight-end */}
</div>
);
}
If you don't wrap your imported MDX with MDXContent
, the global scope will not be available.
:::
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 to ensure that your syntax is valid MDX.
Samples of parsing failures
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}>
<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}>
<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 _Markdown_ **works**
</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 _Markdown_ **works**
</BrowserWindow>
</div>
</TabItem>
</Tabs>
**Markdown within a JSX tag never works:**
<Tabs groupId="jsx-and-md">
<TabItem value="Problem">
<div className={styles.wrappingBlock}>
<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}>
<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}>
<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}>
<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}>
<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}>
<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>
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
Now you can import code snippets from another file as it is:
import CodeBlock from '@theme/CodeBlock';
import MyComponentSource from '!!raw-loader!./myComponent';
<CodeBlock language="jsx">{MyComponentSource}</CodeBlock>
import CodeBlock from '@theme/CodeBlock';
import MyComponentSource from '!!raw-loader!@site/src/pages/examples/_myComponent';
<BrowserWindow>
<CodeBlock language="jsx">{MyComponentSource}</CodeBlock>
</BrowserWindow>
See using code blocks 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.
:::
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.
<span>Hello {props.name}</span>
This is text some content from `_markdown-partial-example.mdx`.
import PartialExample from './_markdown-partial-example.mdx';
<PartialExample name="Sebastien" />
import PartialExample from './_markdown-partial-example.mdx';
<BrowserWindow>
<PartialExample name="Sebastien" />
</BrowserWindow>
This way, you can reuse content among multiple pages and avoid duplicating materials.
:::caution
Currently, the table of contents does not contain the imported Markdown headings. This is a technical limitation that we are trying to solve (issue).
:::
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 firsth1
heading in the Markdown text. It'sundefined
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>
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>