After finishing a project that involved migrating a Sculpin site to GatsbyJS using MDX to allow embeding React components in markdown files, we decided to publish a series of posts to share our experience.
Welcome to the first post of the series. In this post, you will learn how to use MDXRenderer and MDXProvider to embed JSX/React components using short-codes.
Why MDX
MDX is Markdown for the component era. It lets you combine JSX with Markdown’s syntax. Yes, you can embed JSX code or even better embed reusable components within your content.
- Powerful: MDX blends markdown and JSX syntax to fit perfectly in JSX-based projects.
- Everything is a component: Import JSX components and render them directly in your MDX document.
- Customizable: Decide which component is rendered for each markdown element.
- Markdown-based: The simplicity and elegance of Markdown remain, you interleave JSX only when you want to.
- Blazingly blazing fast: MDX has no runtime, all compilation occurs during the build stage.
Adding MDX dependency using npm
Run this on your Gatsby project root directory.
npm install --save gatsby-mdx @mdx-js/mdx@next @mdx-js/react@next
Configuring the gatsby-mdx plugin
Using this configuration on the project gatsby-config.js
file, we ensure any file with the extension .md
and .mdx
is transformed by the gatsby-mdx
plugin.
plugins: [
{
resolve: `gatsby-source-filesystem`,
options: {
path: `${__dirname}/src/content`,
name: `content`,
},
},
{
resolve: `gatsby-mdx`,
options: {
extensions: [".mdx", ".md"],
gatsbyRemarkPlugins: [
{
resolve: "gatsby-remark-images",
options: {
maxWidth: 1035,
sizeByPixelDensity: true,
showCaptions: true,
linkImagesToOriginal: false,
},
},
{
resolve: `gatsby-remark-prismjs`,
options: {
classPrefix: "language-",
inlineCodeMarker: null,
aliases: {},
},
},
{
resolve: "gatsby-remark-external-links",
options: {
target: null,
rel: "nofollow noopener noreferrer external",
},
},
`gatsby-remark-slug`,
],
},
},
],
You can add any other markdown remark plugin using the gatsbyRemarkPlugins
array.
Take note to query MDX content, the gatsby-source-filesystem
plugin must be included to discover files from the filesystem.
The components
Nothing new here, but for the sake of the example, we are going to assume we have our components at the src/components
directory.
Any component coming from a javascript file would be written like any other React component.
- src/components/alert.js
import React from "react"
const Alert = ({ title, type, children }) => {
return (
<div className={`alert alert-${type}`}>
<h4 className={type}>{title}</h4>
{children}
</div>
)
}
export default Alert
- src/components/callout.js
import React from "react"
import ExternalLink from "./externalLink"
const Callout = ({ type, children, title, link }) => {
return (
<div className="enablement">
<h4 className={type}>
<ExternalLink text={title} link={link} />
</h4>
{children}
</div>
)
}
export default Callout
- src/components/layout.js
import React from "react"
import Header from "./header"
import Footer from "./footer"
const Layout = ({ children }) => {
return (
<>
<Header />
{children}
<Footer />
)
}
export default Layout
Adding components using short-codes
Within our template file src/templates/doc.js
we have this code:
import React from "react"
import { graphql } from "gatsby"
import MDXRenderer from "gatsby-mdx/mdx-renderer"
import { MDXProvider } from "@mdx-js/react"
import Layout from "../components/layout"
import Alert from "../components/alert"
import Callout from "../components/callout"
const shortcodes = {
Alert,
Callout,
}
render() {
const node = this.props.data.mdx;
return (
<Layout>
<MDXProvider components={shortcodes}>
<MDXRenderer>{node.code.body}</MDXRenderer>
</MDXProvider>
</Layout>
)
}
export default DocTemplate
By providing an array of shortcodes
and passing them to the MDXProvider
component, we can support globally available components to avoid using import
or require
within our md/mdx files.
The GraphQL query
export const pageQuery = graphql`
query DocBySlug($slug: String!) {
mdx(fields: { slug: { eq: $slug } }) {
code {
body
}
}
}
`
As you can see, we are passing the slug
value to render query a single page but more important for the purpose of this example instead of using the html
rendered version of the markdownRemark
we are retrieving code
> body
from mdx
to pass as children to the MDXRenderer component.
The markdown files
This example shows how the content of one md
file looks like.
---
title: Lorem ipsum
authors: [foo, bar, baz]
---
<Callout title="Your Title here" link="https://example.org">
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
</Callout>
<Alert title="Note" type="info">
Aenean velit risus, auctor eu nunc vitae, gravida condimentum leo.
</Alert>
<Alert title="Note" type="danger">
Proin et imperdiet sapien, non commodo lectus.
</Alert>
And this image shows the rendered version of the embedded components.
### Are you interested in knowing more about GatsbyJS?
This series does not end here. We invite you to come back as we continue. We will be sharing some nice tips & tricks that we learned during the development of this project. This is a list of the topics we are planning to share:
- How to create a
text/markdown
node to process markdown content. - How to query all posts by an author when you have multiple authors per markdown file on your front-matter field.
- How to load templates based on a front-matter field.
- How to create page navigation based on a directory structure. Using parent directory as the base and showing all of the children files as clickable link pages.