MDX v0.20.3


Getting Started

If you have an existing project you want to integrate MDX with, check out the installation guides.

Table of contents

Hello World

The smallest MDX example looks like this:

# Hello, world!

It displays a heading saying “Hello, world!” on the page. You could also write it like so:

<h1>Hello, world!</h1>

This displays the same heading.

Syntax

MDX syntax can be boiled down to being JSX in Markdown. It’s a superset of Markdown syntax that also supports importing, exporting, and JSX.

Markdown

Traditionally, Markdown is used to generate HTML. Many developers like writing markup in Markdown as it often looks more like what’s intended and it is typically terser. Instead of the following HTML:

<blockquote>
<p>A blockquote with <em>some</em> emphasis.</p>
</blockquote>

You can write the equivalent in Markdown (or MDX) like so:

> A blockquote with *some* emphasis.

Markdown is good for content. MDX supports standard Markdown syntax. It’s important to understand Markdown in order to learn MDX.

JSX

Recently, more and more developers have started using JSX to generate HTML markup. JSX is typically combined with a frontend framework like React or Vue. These frameworks add support for components, which let you change repeating things like the following markup:

<h2>Hello, Venus!</h2>
<h2>Hello, Mars!</h2>

…to JSX (or MDX) like this:

<Welcome name="Venus" />
<Welcome name="Mars" />

JSX is good for components. It makes repeating things more clear and allows for separation of concerns. MDX fully supports JSX syntax. Any line that start with the < characters starts a JSX block.

MDX

We love HTML, but we’ve created MDX to let you combine the benefits of Markdown with the benefits of JSX. The following example shows how they can be combined. It’s interactive so go ahead and change the code!

Below is a JSX block

Try making this heading have the color green

# Below is a JSX block

<div style={{ padding: '10px 30px', backgroundColor: 'tomato' }}>
  <h2>Try making this heading have the color green</h2>
</div>

MDX supports two more features: imports and exports.

Imports

import (ES2015) can be used to import components, data, and documents.

Components

You can import components, such as your own or from rebass, like so:

import { Box, Heading, Text } from 'rebass'
# Here is a JSX block
It is using imported components!
<Box>
<Heading>Here's a JSX block</Heading>
<Text>It's pretty neat</Text>
</Box>
Data

You can also import data that you want to display:

import snowfallData from './snowfall.json'
import BarChart from './charts/BarChart'
# Recent snowfall trends
2019 has been a particularly snowy year when compared to the last decade.
<BarChart data={snowfallData} />
Documents

You can embed MDX documents in other documents. This is also known as transclusion. You can achieve this by importing an .mdx (or .md) file:

import License from './license.md'
import Contributing from './docs/contributing.mdx'
# Hello, world!
<License />
---
<Contributing />

Exports

export (ES2015) can be used to export data and components. For example, you can export metadata like which layout to use or the authors of a document. It’s a mechanism for an imported MDX file to communicate with the thing that imports it.

Say we import our MDX file, using webpack and React, like so:

// index.js
import React from 'react'
import MDXDocument, { metadata } from 'posts/post.mdx'
export default () => (
<>
<MDXDocument />
<footer>
<p>By: {metadata.authors.map(author => author.name).join(', ') + '.'}</p>
</footer>
</>
)

And our MDX file looks as follows (note the export):

// posts/post.mdx
import { sue, fred } from '../data/authors'
export const metadata = {
authors: [sue, fred]
}
# Post about MDX
MDX is a JSX in Markdown loader, parser, and renderer for ambitious projects.

After bundling and evaluating, we could get something like this:

<h1>Post about MDX</h1>
<p>MDX is a JSX in Markdown loader, parser, and renderer for ambitious projects.</p>
<footer><p>By: Sue, Fred.</p></footer>

This is similar to what frontmatter allows in Markdown, but instead of supporting only data in something like YAML, MDX lets you use richer JavaScript structures.

Working with components

In addition to rendering components inline, you can also pass in components to be used instead of the default HTML elements that Markdown compiles to. This allows you to use your existing components and even CSS-in-JS like styled-components.

The components object is a mapping between the HTML name and the desired component you’d like to render.

// src/App.js
import React from 'react'
import Hello from '../hello.mdx'
const MyH1 = props => <h1 style={{ color: 'tomato' }} {...props} />
const MyParagraph = props => <p style={{ fontSize: '18px', lineHeight: 1.6 }} />
const components = {
h1: MyH1,
p: MyParagraph
}
export default () => <Hello components={components} />

You can also import your components from another location like your UI library:

import React from 'react'
import Hello from '../hello.mdx'
import {
Text,
Heading,
Code,
InlineCode
} from '../my-ui-library'
export default () =>
<Hello
components={{
h1: Heading,
p: Text,
code: Code,
inlineCode: InlineCode
}}
/>

With the above, the Heading component will be rendered for any h1, Text for p elements, and so on.

In addition to HTML elements, there is one special mapping: inlineCode can be used for code inside paragraphs, tables, etc.

See the Table of components for supported names.

MDXProvider

If you’re using an app layout that wraps your application, you can use the MDXProvider to only pass your components in one place:

// src/App.js
import React from 'react'
import { MDXProvider } from '@mdx-js/tag'
import { Heading, Text, Pre, Code, Table } from './components'
const components = {
h1: Heading.H1,
h2: Heading.H2,
// …
p: Text,
code: Pre,
inlineCode: Code
}
export default props =>
<MDXProvider components={components}>
<main {...props} />
</MDXProvider>

This allows you to remove duplicated component passing and importing. It will typically go in layout files.

Using the wrapper

The MDXProvider has a special wrapper key that you can use in the component mapping. With your wrapper component you can set the layout of your document, inject styling, or even manipulate the children passed to the component.

// src/App.js
import React from 'react'
import { MDXProvider } from '@mdx-js/tag'
const Wrapper = props => <main style={{ padding: '20px', backgroundColor: 'tomato' }} {...props} />
export default ({ children }) =>
<MDXProvider components={{ wrapper: Wrapper }}>
{children}
</MDXProvider>

If you would like to see more advanced usage, see the wrapper customization guide.

Default exports

Sometimes from an MDX file you might want to override the wrapper. This is especially useful when you want to override layout for a single entry point at the page level. To achieve this you can use the ES default export and it will wrap your MDX document instead of the wrapper passed to MDXProvider.

You can declare a default export as a function:

import Layout from './Layout'
export default ({ children }) => <Layout some='metadata' >{children}</Layout>
# Hello, world!

Or directly as a component:

import Layout from './Layout'
export default Layout
# Hello, world!

Either works. Whatever you prefer!

Table of components

MDXProvider uses React Context to provide the component mapping internally to MDX when it renders. The following components are rendered from Markdown, so these can be keys in the component object you pass to MDXProvider.

TagNameSyntax
pParagraph
h1Heading 1#
h2Heading 2##
h3Heading 3###
h4Heading 4####
h5Heading 5#####
h6Heading 6######
thematicBreakThematic break***
blockquoteBlockquote>
ulList-
olOrdered list1.
liList item
tableTable--- | --- | ---
trTable rowThis | is | a | table row
td/thTable cell
prePre
codeCode
emEmphasis_emphasis_
strongStrong**strong**
deleteDelete~~strikethrough~~
codeInlineCode
hrBreak---
aLink<https://mdxjs.com> or [MDX](https://mdxjs.com)
imgImage![alt](https://mdx-logo.now.sh)

Installation guides

Now that we’ve gone over how MDX works, you’re ready to get installing.

Scaffold out an app

If you’re the type of person that wants to scaffold out an app quickly and start playing around you can use npm init:

Do it yourself

If your favorite bundler or framework isn’t listed above, or if you have a custom use case, you can of course do it yourself. The below rendering function is what we use for our MDX integration tests:

const babel = require('@babel/core')
const React = require('react')
const {renderToStaticMarkup} = require('react-dom/server')
const transform = code =>
babel.transform(code, {
plugins: [
'@babel/plugin-transform-react-jsx',
'@babel/plugin-proposal-object-rest-spread'
]
}).code
const renderWithReact = async mdxCode => {
const jsx = await mdx(mdxCode, {skipExport: true})
const code = transform(jsx)
const scope = {mdx: createElement}
const fn = new Function(
'React',
...Object.keys(scope),
`${code}; return React.createElement(MDXContent)`
)
const element = fn(React, ...Object.values(scope))
const components = {
h1: ({children}) =>
React.createElement('h1', {style: {color: 'tomato'}}, children)
}
const elementWithProvider = React.createElement(
MDXProvider,
{components},
element
)
return renderToStaticMarkup(elementWithProvider)
}