gatsby-plugin-mdx
is the official integration for using MDX with Gatsby.
MDX is markdown for the component era. It lets you write JSX embedded inside markdown. It’s a great combination because it allows you to use markdown’s often terse syntax (such as # heading
) for the little things and JSX for more advanced components.
npm install gatsby-plugin-mdx gatsby-source-filesystem @mdx-js/react
After installing gatsby-plugin-mdx
you can add it to your plugins list in your
gatsby-config.js
. You'll also want to configure gatsby-source-filesytem
to point at your src/pages
directory (even if you don't want to create MDX pages from src/pages
).
module.exports = {
plugins: [
`gatsby-plugin-mdx`,
{
resolve: `gatsby-source-filesystem`,
options: {
name: `pages`,
path: `${__dirname}/src/pages`,
},
},
],
}
By default, this configuration will allow you to automatically create pages with .mdx
files in src/pages
and will process any Gatsby nodes with Markdown media types into MDX content.
Note that gatsby-plugin-mdx
requires gatsby-source-filesystem
to be present and configured to process local markdown files in order to generate the resulting Gatsby nodes.
To automatically create pages from MDX files outside of src/pages
you'll need to configure gatsby-plugin-page-creator
and gatsby-source-filesystem
to point to this folder of files.
module.exports = {
plugins: [
`gatsby-plugin-mdx`,
{
resolve: `gatsby-source-filesystem`,
options: {
name: `posts`,
path: `${__dirname}/src/posts`,
},
},
{
resolve: `gatsby-plugin-page-creator`,
options: {
path: `${__dirname}/src/posts`,
},
},
],
}
Also check out the guide Adding MDX Pages for more details.
gatsby-plugin-mdx
exposes a configuration API that can be used similarly to any other Gatsby plugin. You can define MDX extensions, layouts, global scope, and more.
Key | Default | Description |
---|---|---|
extensions |
[".mdx"] |
Configure the file extensions that gatsby-plugin-mdx will process |
gatsbyRemarkPlugins |
[] |
Use Gatsby-specific remark plugins |
mdxOptions |
{} |
Options directly passed to compile() of @mdx-js/mdx |
By default, only files with the .mdx
file extension are treated as MDX when
using gatsby-source-filesystem
. To use .md
or other file extensions, you can
define an array of file extensions in the gatsby-plugin-mdx
section of your
gatsby-config.js
.
module.exports = {
plugins: [
{
resolve: `gatsby-plugin-mdx`,
options: {
extensions: [`.mdx`, `.md`],
},
},
],
}
This config option is used for compatibility with a set of plugins many people use with remark that require the Gatsby environment to function properly. In some cases, like gatsby-remark-prismjs, it makes more sense to use a library like prism-react-renderer to render codeblocks using a React component. In other cases, like gatsby-remark-images, the interaction with the Gatsby APIs is well deserved because the images can be optimized by Gatsby and you should continue using it.
module.exports = {
plugins: [
{
resolve: `gatsby-plugin-mdx`,
options: {
gatsbyRemarkPlugins: [
{
resolve: `gatsby-remark-images`,
options: {
maxWidth: 590,
},
},
],
},
},
],
}
Using a string reference is also supported for gatsbyRemarkPlugins
.
gatsbyRemarkPlugins: [`gatsby-remark-images`]
These configuration options are directly passed into the MDX compiler.
See all available options in the official documentation of @mdx-js/mdx
.
module.exports = {
plugins: [
{
resolve: `gatsby-plugin-mdx`,
options: {
mdxOptions: {
remarkPlugins: [
require("remark-abbr"),
// To pass options, use a 2-element array with the
// configuration in an object in the second element
[require("remark-external-links"), { target: false }],
],
rehypePlugins: [
// Generate heading ids for rehype-autolink-headings
require("rehype-slug"),
// To pass options, use a 2-element array with the
// configuration in an object in the second element
[require("rehype-autolink-headings"), { behavior: "wrap" }],
],
},
},
},
],
}
When importing a React component into your MDX, you can import it using the import
statement like in JavaScript.
import { SketchPicker } from "react-color"
# Hello, world!
Here's a color picker!
<SketchPicker />
Note: You should restart gatsby develop
to update imports in MDX files. Otherwise, you'll get a ReferenceError
for new imports. You can use the shortcodes approach if that is an issue for you.
You can use regular layout components to apply layout to your sub pages.
To inject them, you have several options:
- Use the
wrapPageElement
API including its SSR counterpart. - Add an
export default Layout
statement to your MDX file, see MDX documentation on Layout. - When using the
createPage
action to programatically create pages, you should use the following URI pattern for your page component:your-layout-component.js?__contentFilePath=absolute-path-to-your-mdx-file.mdx
. To learn more about this, head to the programmatically creating pages guide.
Read the MDX documentation on programmatically creating pages to learn more.
In your GraphQL schema, you will discover several additional data related to your MDX content. While your local GraphiQL will give you the most recent data, here are the most relevant properties of the Mdx
entities:
Property | Description |
---|---|
frontmatter | Sub-entity with all frontmatter data. Regular Gatsby transformations apply, like you can format dates directly within the query. |
excerpt | A pruned variant of your content. By default trimmed to 140 characters. Based on rehype-infer-description-meta |
tableOfContents | Generates a recursive object structure to reflect a table of contents. Based on mdast-util-toc |
TODO
TODO
TODO
TODO
MDX and gatsby-plugin-mdx
use components for different things like rendering and component mappings.
MDXProvider
is a React component that allows you to replace the
rendering of tags in MDX content. It does this by providing a list of
components via context to the internal MDXTag
component that handles
rendering of base tags like p
and h1
.
import { MDXProvider } from "@mdx-js/react"
const MyH1 = props => <h1 style={{ color: "tomato" }} {...props} />
const MyParagraph = props => (
<p style={{ fontSize: "18px", lineHeight: 1.6 }} {...props} />
)
const components = {
h1: MyH1,
p: MyParagraph,
}
export const ComponentsWrapper = ({ children }) => (
<MDXProvider components={components}>{children}</MDXProvider>
)
The following components can be customized with the MDXProvider
:
Tag | Name | Syntax |
---|---|---|
p |
Paragraph | |
h1 |
Heading 1 | # |
h2 |
Heading 2 | ## |
h3 |
Heading 3 | ### |
h4 |
Heading 4 | #### |
h5 |
Heading 5 | ##### |
h6 |
Heading 6 | ###### |
thematicBreak |
Thematic break | *** |
blockquote |
Blockquote | > |
ul |
List | - |
ol |
Ordered list | 1. |
li |
List item | |
table |
Table | `--- |
tr |
Table row | `This |
td /th |
Table cell | |
pre |
Pre | |
code |
Code | |
em |
Emphasis | _emphasis_ |
strong |
Strong | **strong** |
delete |
Delete | ~~strikethrough~~ |
hr |
Break | --- |
a |
Link | <https://mdxjs.com> or [MDX](https://mdxjs.com) |
img |
Image | ![alt](https://mdx-logo.now.sh) |
It's important to define the components
you pass in a stable way
so that the references don't change if you want to be able to navigate
to a hash. That's why we defined components
outside of any render
functions in these examples.
If you want to allow usage of a component from anywhere (often referred to as a shortcode), you can pass it to the MDXProvider.
import React from "react"
import { MDXProvider } from "@mdx-js/react"
import { Link } from "gatsby"
import { YouTube, Twitter, TomatoBox } from "./ui"
const shortcodes = { Link, YouTube, Twitter, TomatoBox }
export const Layout = ({ children }) => (
<MDXProvider components={shortcodes}>{children}</MDXProvider>
)
Then, in any MDX file, you can navigate using Link
and render YouTube
, Twitter
, and TomatoBox
components without
an import.
# Hello, world!
Here's a YouTube embed
<TomatoBox>
<YouTube id="123abc" />
</TomatoBox>
Read more about injecting your own components in the official MDX provider guide.
gatsby-plugin-mdx@^4.0.0
is a complete rewrite of the original plugin with the goal of making the plugin faster, compatible with MDX v2, leaner, and more maintainable. While doing this rewrite we took the opportunity to fix long-standing issues and remove some functionalities that we now think should be handled by the user, not the plugin. In doing so there will be of course breaking changes you'll have to handle – but with the help of this migration guide and the codemods you'll be on the new version in no time!
Please Note: Loading MDX from other sources as the file system is not yet supported in gatsby-plugin-mdx@^4.0.0
.
During the Release Candidate phase, use these install instructions:
npm remove @mdx-js/mdx
npm install --save-exact gatsby@mdxv4-rc gatsby-plugin-mdx@mdxv4-rc @mdx-js/react@latest
If you used any related plugins like gatsby-remark-images
, also update them to their @mdxv4-rc
version (if available).
- Move your
remarkPlugins
andrehypePlugins
keys into the newmdxOptions
config option:module.exports = { plugins: [ { resolve: `gatsby-plugin-mdx`, options: { - remarkPlugins: [], - rehypePlugins: [], + mdxOptions: { + remarkPlugins: [], + rehypePlugins: [], + }, }, }, ], }
- There's a new option called
mdxOptions
which is passed directly to the MDX compiler. See all available options in the official documentation of@mdx-js/mdx
. - Only
extensions
,gatsbyRemarkPlugins
, andmdxOptions
exist as options now. Every other option got removed, includingdefaultLayouts
. See the layouts guide to learn how to use layouts withgatsby-plugin-mdx@^4.0.0
. - Make sure that any
gatsby-remark-*
plugins are only listed inside thegatsbyRemarkPlugins
array ofgatsby-plugin-mdx
, not inside theplugins
array ofgatsby-config
or in any other place.
In most cases the changes necessary here are related to the removal of defaultLayouts
option and the new way how layouts are done. See the layouts guide to learn how to use layouts with gatsby-plugin-mdx@^4.0.0
.
You'll need to do two things to continue using your old layout file:
- You need to query the absolute path to the MDX file
- You have to attach this MDX file via the
__contentFilePath
query param to your layout file
actions.createPage({
- component: `/path/to/template.js`,
+ component: `/path/to/template.js?__contentFilePath=/path/to/content.mdx`,
})
Or a more complete example:
const { data } = await graphql(`
{
allMdx {
nodes {
id
frontmatter {
slug
}
// highlight-start
parent {
... on File {
absolutePath
}
}
// highlight-end
}
}
}
`)
data.allMdx.nodes.forEach(node => {
actions.createPage({
path: node.frontmatter.slug,
component: `/path/to/your/template.js?__contentFilePath=${node.parent.absolutePath}`, // highlight-line
context: {
id: node.id,
},
})
})
You'll also need to update your template file itself, see the next section Updating page templates.
Note: You could also directly pass the MDX file to the component
like this:
actions.createPage({
component: `/path/to/content.mdx`,
})
However, we'd recommend placing such files into the src/pages
directory and gatsby-plugin-mdx
will handle the page creation itself.
- Instead of querying for the
body
on the MDX node, the page template now receives the transformed MDX aschildren
property. - You no longer need to use
<MDXRenderer>
as you can use{children}
directly. - If you have given your query a name (e.g.
query PostTemplate
) you have to remove the name. Gatsby automatically creates a name internally, but if a name is provided you'll see a "Duplicate query" warning.
import React from "react"
import { graphql } from "gatsby"
- import { MDXRenderer } from "gatsby-plugin-mdx"
- function PostTemplate({ data: { mdx } }) {
+ function PostTemplate({ data: { mdx }, children }) {
return (
<main>
<h1>{mdx.frontmatter.title}</h1>
- <MDXRenderer>
- {mdx.body}
- </MDXRenderer>
+ {children}
</main>
)
}
export const pageQuery = graphql`
- query PostTemplate($id: String!) {
+ query ($id: String!) {
mdx(id: { eq: $id }) {
frontmatter {
title
}
- body
}
}
`
export default PostTemplate
As MDX v2 changed the way it handles content you might need to update your MDX files to be valid MDX now. See their Update MDX content guide for all details.
In our testing, most of the time the issue were curly brackets that needed to be escaped with backticks:
- You can upload this to Git{Hub,Lab}
+ You can upload this to `Git{Hub,Lab}`
- Removed plugin options:
defaultLayouts
,mediaTypes
,lessBabel
,shouldBlockNodeFromTransformation
,commonmark
- Moved plugin options
remarkPlugins
andrehypePlugins
intomdxOptions
- Removed
timeToRead
,rawBody
,slug
,headings
,html
,mdxAST
,wordCount
,fileAbsolutePath
from the query result. You can check Extending the GraphQL MDX nodes to learn how to re-implement some of them on your own. gatsby-plugin-mdx
only applies to local files (that are sourced withgatsby-source-filesystem
)
Before MDX, some of the benefits of writing Markdown were lost when integrating with JSX. Implementations were often template string-based which required lots of escaping and cumbersome syntax.
MDX seeks to make writing with Markdown and JSX simpler while being more expressive. Writing is fun again when you combine components, that can even be dynamic or load data, with the simplicity of Markdown for long-form content.