MDX integration with Gatsby for ambitious projects.
npm install gatsby-mdx @mdx-js/mdx @mdx-js/tagthen add gatsby-mdx to your gatsby-config.js in the plugins section.
module.exports = {
siteMetadata: {
title: `My Ambitious Project`
},
plugins: [`gatsby-mdx`]
};Note: gatsby-mdx is only compatible with Gatsby version 2 or
newer.
Add an .mdx file in the src/pages directory. It "Just Works".
# My first MDX Page
some awesome content
gatsby-mdx can apply to different file extensions. By default it
conservatively applies to only .mdx files, but can also be made to apply to
.md files.
module.exports = {
plugins: [
{
resolve: `gatsby-mdx`,
options: {
extensions: [".mdx", ".md"]
}
}
]
};MDX supports layouts using the default export as such:
export default ({ children }) => (
<div>
<h1>My Layout</h1>
<div>{children}</div>
</div>
)
# My MDX
some contentor as an import:
import PageLayout from './src/components/page-layout';
export PageLayout
# My MDX
some contentSometimes you don't want to include the layout in every file, so gatsby-mdx
offers the option to set default layouts in the gatsby-config.js plugin
config. Set the key to the name set in the gatsby-source-filesystem config.
If no matching default layout is found, the default default layout is used.
You can also set options.defaultLayout if you only want to use one layout for
all MDX pages.
module.exports = {
siteMetadata: {
title: `Gatsby MDX Kitchen Sink`
},
plugins: [
{
resolve: `gatsby-mdx`,
options: {
defaultLayouts: {
posts: require.resolve("./src/components/posts-layout.js"),
default: require.resolve("./src/components/default-page-layout.js")
}
}
},
{
resolve: `gatsby-source-filesystem`,
options: {
name: `posts`,
path: `${__dirname}/src/posts/`
}
}
]
};MDX files can be queried with allMdx on the root query. Like
gatsby-transformer-remark, this plugin adds fields to the Mdx node including
excerpt, headings, timeToRead, and wordCount.
All static exports – values that would be valid JSON – are queryable through the
exports field.
query MDXQuery {
allMdx {
edges {
node {
relativePath
fileAbsolutePath
fileNode {
name
}
timeToRead
frontmatter {
title
}
exports {
author
}
}
}
}
}Pages can be created programmatically by combining gatsby-source-filesystem in
gatsby-config.js with some createPage calls.
# gatsby-config.js
module.exports = {
plugins: [
`gatsby-mdx`,
{
resolve: "gatsby-source-filesystem",
options: {
name: "posts",
path: `${__dirname}/content/`
}
}
]
};# gatsby-node.js
exports.createPages = ({ graphql, actions }) => {
const { createPage } = actions;
return new Promise((resolve, reject) => {
resolve(
graphql(
`
{
allMdx {
edges {
node {
fileAbsolutePath
fileNode {
name
}
}
}
}
}
`
).then(result => {
if (result.errors) {
console.log(result.errors);
reject(result.errors);
}
// Create blog posts pages.
result.data.allMdx.edges.forEach(({ node }) => {
createPage({
path: `/non-page/${node.fileNode.name}`,
component: node.fileAbsolutePath, //blogPost,
context: { absPath: node.absolutePath }
});
});
})
);
});
};Using MDX, you can replace every HTML element that Markdown renders with a custom implementation. This allows you to use a set of design system components when rendering Markdown.
// src/components/layout.js
import { MDXProvider } from "@mdx-js/tag";
import * as DesignSystem from "your-design-system";
export default function Layout({ children }) {
return (
<MDXProvider
components={{
// Map HTML element tag to React component
h1: DesignSystem.H1,
h2: DesignSystem.H2,
h3: DesignSystem.H3,
// Or define component inline
p: props => <p {...props} style={{ color: "rebeccapurple" }} />
}}
>
{children}
</MDXProvider>
);
}To make every markdown code block an editable live example, you can pass in a
custom code element to MDXProvider.
import React, {Component} from "react";
import { LiveProvider, LiveEditor, LiveError, LivePreview } from "react-live";
import { MDXProvider } from '@mdx-js/tag'
const MyCodeComponent = ({ children, ...props }) => (
<LiveProvider code={children}>
<LiveEditor />
<LiveError />
<LivePreview />
</LiveProvider>
);
export default MyPageLayout extends Component {
render() {
return <MDXProvider components={{code: MyCodeComponent}}>
<div>{this.props.children}</div>
</MDXProvider>
}
}