Skip to content
🎉MDX Conf — August 24th, 2020
MDX logo
v2.0.0-next.9

API

MDX (the library), at its core, transforms MDX (the syntax) to JSX. It receives an MDX string and outputs a JSX string. It does this by parsing the MDX document to a syntax tree and then generates a JSX document from that tree. The JSX document is typically evaluated later through something like Babel or webpack. You can extend MDX by passing plugins that change the tree to customize how the JSX string is created.

Table of Contents

Async API

By default, MDX is asynchronous because plugins can be asynchronous themselves! This means that plugins can request data, read from the file system. Anything! You should use the async API, unless you have very good reasons to use the sync API.

You can use the library directly:

import {mdx} from '@mdx-js/mdx'

const content = `
# Hello, world!
`

const transpile = async () => {
  const jsx = await mdx(content)
  return jsx
}

transpile().then(console.log)

With more complex input, we can see more interesting output:

Input
import TomatoBox from 'tomato-box'

export const author = "Fred Flintstone"
export default props => <main {...props} />

# Hello, world!

Here is a paragraph

<TomatoBox />
Output
import TomatoBox from 'tomato-box'

export const author = 'Fred Flintstone'

const layoutProps = {
  author
}

export default function MDXContent({components, ...props}) {
  return (
    <MDXLayout
      {...layoutProps}
      {...props}
      components={components}
      mdxType="MDXLayout"
    >
      <h1>{`Hello, world!`}</h1>
      <p>{`Here is a paragraph`}</p>
      <TomatoBox mdxType="TomatoBox" />
    </MDXLayout>
  )
}

MDXContent.isMDXComponent = true

This is pretty powerful because the output is rather readable. The only weird part is the string escaping from Markdown content:

<h1>{`Hello, world!`}</h1>

It’s not super readable, but it makes sure you can write content that wouldn’t be valid JSX syntax!

Sync API

MDX processes everything asynchronously by default. In certain cases this behavior might not be desirable.

If you’re using the MDX library directly, you might want to process an MDX string synchronously. It’s important to note that if you have any async plugins, they will be ignored.

import fs from 'fs'
import {mdx} from '@mdx-js/mdx'

const mdxText = fs.readFileSync('hello.mdx', 'utf8')

const jsx = mdx.sync(mdxText)

MDX’s runtime package has example usage.

Compiler

If you want to extract data from a MDX file, you can access the compiler directly:

import {createCompiler} from '@mdx-js/mdx'
import detectFrontmatter from 'remark-frontmatter'
import vfile from 'vfile'
import visit from 'unist-util-visit'
import remove from 'unist-util-remove'
import yaml from 'yaml'

const file = vfile(
  `
---
title: Hello, MDX
---

I &lt;3 Markdown and JSX {/* < must be escaped for \`@mdx-js/mdx@2\` */}
`.trimStart() // there should be no space before frontmatter for `remark-frontmatter@3`, this can be guaranteed with `trimStart`
)

function extractFrontmatter() {
  return function transformer(tree, file) {
    visit(tree, 'yaml', function visitor(node) {
      file.data.frontmatter = yaml.parse(node.value)
    })
    remove(tree, 'yaml')
  }
}

mdxCompiler = createCompiler({
  remarkPlugins: [detectFrontmatter, extractFrontmatter]
})

mdxCompiler.process(file, function done(err, file) {
  console.log(file.data.frontmatter)
  // { title: "Hello, MDX" }
})
Edit this page on GitHub