--- id: 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. ::: Check out the [MDX docs](https://mdxjs.com/) to see what other fancy stuff you can do with MDX. ### Exporting components {#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. ```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.mdx#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.mdx) and [translating](../../i18n/i18n-tutorial.mdx). ::: 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" import React from 'react'; 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> ``` :::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](#mdx-component-scope) ::: ### MDX component scope {#mdx-component-scope} Apart from [importing a component](#importing-components) and [exporting a component](#exporting-components), 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: ```md - 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](../../swizzling.mdx) 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](./markdown-features-code-blocks.mdx)). If you want to register extra tag names (like the `<Highlight>` tag above), you should consider [wrapping `@theme/MDXComponents`](../../swizzling.mdx#wrapping), 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: ```js title="src/theme/MDXComponents.js" 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, }; ``` And now, you can freely use `<Highlight>` in every page, without writing the import statement: ```md I can conveniently use <Highlight color="#25c2a0">Docusaurus green</Highlight> everywhere! ``` ```mdx-code-block <BrowserWindow> I can conveniently use <Highlight color="#25c2a0">Docusaurus green</Highlight> everywhere! </BrowserWindow> ``` :::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. ::: :::caution This feature is powered by [a wrapper provider](https://mdx-git-renovate-babel-monorepo-mdx.vercel.app/advanced/components#mdxprovider). If you are importing Markdown in a React page, you have to supply this provider yourself through the `MDXContent` theme component. ```jsx title="src/pages/index.js" 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. ::: ### 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. <details> <summary>Samples of parsing failures</summary> **A paragraph starting with a JSX tag will be seen entirely as a JSX string:** ```mdx-code-block <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** ``` ```mdx-code-block </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> ``` ```mdx-code-block </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> ``` ```mdx-code-block </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>** ``` ```mdx-code-block </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}> ``` <!-- prettier-ignore --> ```jsx <div style={{color: 'red'}}> **Bold still doesn't work** </div> ``` ```mdx-code-block </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}> ``` <!-- prettier-ignore --> ```jsx <div style={{color: 'red'}}> **Bold now works** </div> ``` ```mdx-code-block </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}> ``` <!-- prettier-ignore --> ```jsx <div style={{color: 'red'}}> You may think I'm just some text... </div> ``` ```mdx-code-block </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}> ``` <!-- prettier-ignore --> ```jsx <div style={{color: 'red'}}> Now I'm actually just text </div> ``` ```mdx-code-block </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> ``` 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`. ``` <!-- prettier-ignore-start --> ```jsx title="someOtherDoc.mdx" import PartialExample from './_markdown-partial-example.mdx'; <PartialExample name="Sebastien" /> ``` <!-- prettier-ignore-end --> ```mdx-code-block 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](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-toc.mdx#inline-table-of-contents) 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> ```