Mdx and remark are both markdown compilers i.e. they convert markdown to HTML. So, that it can be rendered on the browser. We can also write HTML in our .md
file, as the final result is HTML the compiler will process it as HTML.
Coming to Remark, It gives us the advantage of extending its functionalities using plugins. Mdx is also very similar to remark and icing on the cake is it supports all remark plugins. But what makes it so popular is the ability to process JSX in .md
files. Basically, it converts the markdown files into React components making it eligible for importing and rendering other components.
This MDX’s ability becomes very useful for things like data-visualization in your blog. MDX makes blogging super fun and easy. Now lets see how we can convert an existing gatsby blog to use MDX in place of Remark.
We first need to install the gatsby-plugin-mdx
plugin with its dependencies (@mdx-js/mdx
and @mdx-js/react
) .
npm install gatsby-plugin-mdx @mdx-js/mdx @mdx-js/react
npm remove gatsby-transformer-remark
Go to your gatsby-config.js
file, Replace plugin gatsby-transformer-remark
with gatsby-plugin-mdx
.
--------------------
||gatsby-config.js||
--------------------
- resolve: `gatsby-transformer-remark`
+ resolve: `gatsby-plugin-mdx`
options: {
Most of the sub-plugins of Remark works perfectly with MDX. We just need to change the plugins
option to gatsbyRemarkPlugins
to let MDX know that these are Remark plugins.
--------------------
||gatsby-config.js||
--------------------
resolve: `gatsby-plugin-mdx`
options: {
- plugins: [
+ gatsbyRemarkPlugins: [
Note: You need to add gatsby-remark-images
plugin as both a sub-plugin of
gatsby-plugin-mdx
and a string entry in the plugins array.
--------------------
||gatsby-config.js||
--------------------
module.exports = {
plugins: [
`gatsby-remark-images`, {
resolve: `gatsby-source-filesystem`,
options: {
path: `${__dirname}/content/blog`,
name: `blog`,
},
},
{
resolve: `gatsby-source-filesystem`,
options: {
path: `${__dirname}/content/assets`,
name: `assets`,
},
},
{
resolve: `gatsby-plugin-mdx`,
options: {
extensions: [`.md`, `.mdx`],
gatsbyRemarkPlugins: [
{ resolve: `gatsby-remark-images`, options: { maxWidth: 590, linkImagesToOriginal: true, }, }, {
resolve: `gatsby-remark-copy-linked-files`,
},
{
resolve: `gatsby-remark-smartypants`,
},
{
resolve: `gatsby-plugin-feed`,
},
],
},
},
{
resolve: `gatsby-plugin-sharp`,
},
Change your markdown files extension from .md
to .mdx
. This will allow MDX to recognize and process them with specified configurations.
If you don’t want to change the files extention might be because of large number of files in your project. In this case you can configure MDX plugin to accept both .md
and .mdx
files by adding the extensions
option in gatsby-plugin-mdx
like this.
--------------------
||gatsby-config.js||
--------------------
{
resolve: `gatsby-plugin-mdx`,
options: {
extensions: [`.md`, `.mdx`], },
},
Tip: If you use Vs-code as your code editor. Use this MDX extention for syntax highlighting and bracket matching in MDX files.
In the createPages
API, Replace allMarkdownRemark
with allMdx
.
------------------
||gatsby-node.js||
------------------
exports.createPages = ({ graphql, actions }) => {
const { createPage } = actions;
const blogPost = path.resolve(`./src/templates/blog-post.tsx`);
const blogList = path.resolve(`./src/templates/blog-list.tsx`);
const tagTemplate = path.resolve(`./src/templates/tags.tsx`);
return graphql(
`
{
- allMarkdownRemark(
+ allMdx(
sort: { fields: [frontmatter___date], order: DESC }
limit: 1000
) {
edges {
Also, In the onCreateNode
API. Where it compares the node type to create slugs, there replace MarkdownRemark
to Mdx
.
------------------
||gatsby-node.js||
------------------
exports.onCreateNode = ({ node, actions, getNode }) => {
const { createNodeField } = actions;
- if (node.internal.type === `MarkdownRemark`) {
+ if (node.internal.type === `Mdx`) {
const value = createFilePath({ node, getNode });
if (typeof node.frontmatter.slug !== 'undefined') {
createNodeField({
node,
name: 'slug',
value: node.frontmatter.slug,
});
} else {
In your GraphQL queries, wherever you use allMarkdownRemark
change it to allMdx
and markdownRemark
to mdx
. Also in the mdx
feild in query, change html
to body
.
-------------------------------
||src/templates/blog-post.tsx||
-------------------------------
export const pageQuery = graphql`
query BlogPostBySlug($slug: String!, $tag: [String!]) {
site {
siteMetadata {
siteUrl
}
}
- markdownRemark(fields: { slug: { eq: $slug } }) {
+ mdx(fields: { slug: { eq: $slug } }) {
id
excerpt(pruneLength: 160)
- html
+ body
fields {
slug
}
frontmatter {
title
date(formatString: "DD MMM, YYYY")
description
hasJs
tags
cover {
publicURL
childImageSharp {
fluid(maxWidth: 1170, quality: 100) {
...GatsbyImageSharpFluid_noBase64
}
}
}
}
}
- allMarkdownRemark(
+ allMdx(
limit: 3
sort: { fields: [frontmatter___date], order: DESC }
filter: {
frontmatter: { tags: { in: $tag } }
fields: { slug: { ne: $slug } }
}
) {
edges {
Note: There is a chance that you receive an error at build time by a plugin which is quering for allMarkdownRemark
. I received this error from gatsby-plugin-feed
plugin to resolve this i moved this inside gatsbyRemarkPlugins
option in gatsby-plugin-mdx
from the main plugins array.
Now, In your post-template file import MDXRenderer
component from gatsby-plugin-mdx
.
------------------------------------------------
||src/components/post-details/post-details.tsx||
------------------------------------------------
import _ from 'lodash';
import { MDXRenderer } from 'gatsby-plugin-mdx';import { Link } from 'gatsby';
Finally, Replace dangerouslySetInnerHTML
to a MDXRenderer
component:
------------------------------------------------
||src/components/post-details/post-details.tsx||
------------------------------------------------
<PostDescriptionWrapper className="post_des_wrapper">
- <PostDescription
- dangerouslySetInnerHTML={{ __html: description }}
- className="post_des"
- />
+ <PostDescription className="post_des">
+ <MDXRenderer>{description}</MDXRenderer>
+ </PostDescription>
{tags == null ? null : (
<PostTags>
{tags.map((tag, index) => (
<Link key={index} to={`/tags/${_.kebabCase(tag)}/`}>
{`#${tag}`}
</Link>
))}
</PostTags>
)}
</PostDescriptionWrapper>
Now you can use custom React components or third-party UI elements in your markdown files. Remember that MDX uses JSX in place of HTML. So, make sure that HTML in you markdown file is valid JSX. for example, if you have used class
attribute in HTML component, change it to className
. So, that it doesn’t create a conflict when processed by MDX.