Migrating to MDX

Monica Powell, creator of React Ladies, talks through why someone would migrate from Remark to MDX before going into how to do that and what problems you might run into.


Monica: [0:01] Hi, I'm Monica. I'm a sophomore engineer and community organizer, and I'm passionate about making technology open source, more accessible, and creating a meet up group React Ladies for women and non-binary React developers.

[0:14] I recently started using MDX, and am looking forward to sharing more about my migration to MDX. Why did I migrate from Remark to MDX? I consider my personal site my playground, and I was intrigued by MDX, and thought, "Why not upgrade?" I was hearing other developers talk about the super powers of MDX, and wanted to check it out for myself.

[0:39] I was especially interested in creating more engaging and interactive content, and had seen some fun examples with MDX that had resonated with me. First, I want to talk through how my site was handling transforming Markdown into HTML.

[0:59] I have a Gatsby site. I was using Remark plugins to actually transform the markdown into HTML. A lot of this functionality is set up within the Gatsby config file, which is if you have built a site with Gatsby, that's also where metadata is added usually as well as if there are other plugins that you need to add to your website.

[1:28] Remark plugins can be used for various things. I've used Remark plugins with things like adding anchor tags to each header within an article, processing responsive images, adding syntax highlighting, embedding content, and more.

[1:45] Here is an example of an embedded code pen example. This is actually using Gatsby MDX embed. It is fully functional if you go through the slide using actually interact with this example. The markup that was required is right here where it's using a JSX react component.

[2:10] This can be within a markdown file, or I use more specifically within the MDX file. Another example of using the Gatsby MDX embed is to embed a tweet. I was really excited about the power of MDX. I was ready to get started.

[2:31] The first thing I had to do was get rid of the Gatsby transformer Remark plugin. Instead of the Gatsby plugin MDX, which is the official plugin integration for using MDX with Gatsby. Luckily, MDX also uses Remark under the hood. It is a parse to abstract syntax tree.

[2:53] It is compatible with a lot of the Remark plugins that I was already familiar with before I started working with MDX. Like I said, I had to install some dependencies. The main ones that I installed were the MDX.js. They have a React package as well as the specific Gatsby plugin MDX.

[3:18] I removed the Gatsby transformer Remark plugin because I was no longer using it and then removed all the lingering remnants of that throughout my Gatsby config file. Outside of the configuration, there were additional places where I was referencing all Markdown where I should be referencing MDX because Markdown was no longer a field that existed within my data layer.

[3:51] Within Gatsby, data such as content is accessed through GraphQL and with the migration from Markdown to MDX, the name of the GraphQL nodes that needs to be accessed changed. For the most part, up until now migrating has just involved installing the dependency, doing some time to replace.

[4:15] I thought I had all my bases covered, just finding and replacing Markdown with MDX. However, I ran into an issue where accessing some of the fields that should be available for MDX nodes like body, time to read an answer, and I wasn't sure why.

[4:32] These nodes just weren't available even though that was something that the Gatsby MDX plugin was supposed to be adding to the site's GraphQL layer. In order to debug this issue, I ended up opening the GraphQL playground for my site where it gives me more visibility into what data is available because GraphQL, the schema is self-documenting.

[4:58] If you start typing, it's really clear to see whether or not data is available or not. I was able to confirm that the field's body, time to read etc. were not available. Next, I ended up commenting out all of the Remark plugins that were within my Gatsby config and then I reran the queries in the playground to see that the data that was formerly not available was appearing.

[5:29] I realized that there was some type of conflict within the Gatsby Remark plugins that I had canceled out. I re-added each plugin one by one in order to identify the culprit. Luckily, I only had a handful of different plugins. I was then able to identify that there was a conflict between Gatsby plugin MDX and Gatsby my social card.

[5:55] Since there was a conflict with Gatsby plugin MDX and the plugin used to generate the open graph images for all of my posts, instead of further investigating what was going on in this particular case, I decided to look for an alternative plugin that would not conflict with Gatsby plugin MDX.

[6:21] Another solution, if you don't want to look at an alternative plugin or dive into what the underlying issue is, is sometimes you can just reorder the plugins and resolve conflicts that way. I have seen some plugins have actually noted within their documentation that they should be ordered a certain way.

[6:44] Sometimes they'll say that they should be the last plugin so that there aren't other plugins that are overriding and conflicting with their functionality. In my particular case, I ended up replacing Gatsby plugin my social card with Gatsby Remark twitter card. Then I no longer have the issues that I was experiencing with the data not being available in the GraphQL for the MDX.

[7:16] What have I done with MDX so far? After resolving the issue with retrieving MDX data, it was time to actually get started with using MDX on my site. Some of the ways I've used MDX so far is upgrade quotes of it, so use more customizable JSX instead of markdown.

[7:34] I'm adding a dynamic table of contents. I'm adding call out components and a dynamic slider to a post. I walk through some of these things. First thing I need to do though before using any of these fancy components, I was actually exposing MDX and its components to my blogpost template.

[8:05] Essentially, I have a template for all of my various blogposts. I want to use MDX within my blogpost. In order to do that, I needed to import the MDX provider from JSX and then wrap that around the body of all of my blogposts. Then I also used the MDX render, which is a react component from the Gatsby plugin MDX integration.

[8:33] That takes compiled MDX content and rendered it. It's required if you want to use MDX content that's coming from a GraphQL page query or stat query, which are very popular in the Gatsby ecosystem. You'll notice within the MDX provider that I am passing in...

[8:55] This is a placeholder to pass in components to the MDX provider. Since this provider is wrapped around all of the blogposts, if I create a blogpost and reference a component, that is important in my article template file and within that component object then I'll automatically have it available from my blogpost to access without having to import the component document to the post.

[9:28] It's a good way to handle parsing different components within MDX if you know that you're going to be reusing them across multiple posts that share the same template.

[9:45] Moving into the first component that I created from my longer articles, I like to add a table of contents so that readers can have an overview of what to expect in a particular article and easily jump around from the beginning of the article to a different part, if there is a particular section that they're interested in, having anchor links to get them there.

[10:09] I like adding table of contents so much that I was manually writing markdown render table of contents within my articles. Here is a gist of what the table of contents for individual post ended up looking like before and after I use MDX. After implementing MDX, adding a table of contents became just a single one-line reference to a component.

[10:36] Instead of having to write up 10 lines to detail the different sections, I can import table of contents component and parse in a table of contents prop, which is available within the post, into the table of contents as a heading. Then that component will have all the information that it needs to render.

[11:01] Let's take a look at some more of the code. In order to render the table of contents, I wanted to have a component that is going to take all of those headings that we parsed in as props. Then for each heading, it will have a title and a URL.

[11:23] That URL is an anchor tag and so it's mapping through all the headers and displaying them as list of items with links. I have another plugin that handles actually adding the autolinks to each of the headers. Then within the article template, which we saw earlier, I'm now importing the table of contents component.

[11:50] This component is accessible to any blogpost. I also updated the post queries to pars in the table of contents field for the post as a prop so then that data can be available to the table of contents component. The table of contents field is a default, which is available with the Gatsby MDX configuration.

[12:18] They actually return data that shows you for any given article all of the headers. It nests down into sub-headers as well. There's a def field. In my use case, I only cared about the first layer, but I will share in the resources and article if you're interested in recursively rendering the table of contents so that it can nest 20 layers if that's required.

[12:49] Something cool I saw around the web was folks were using callout banners to make certain contents stand out. Within my blog, I had a quote, like a markdown quote that I was using on my blog that had one style, but it looked similar to these callout banners.

[13:10] I was eager to explore adding callouts to my website so that I could have different varieties of styles. This was definitely inspired by Prince, @Prince.Dove, who wrote a great article about how to add callout banners. Like I said before I was using MDX, I was using just the markdown quote syntax in writing something like the first block.

[13:45] Once I migrated to MDX and have these fancy callouts. I'm using a callout component and then parsing in different variants. I have a danger variant, garden, and book. Usually, I'll put general things, they won't have a specific variant. If there is some type error message or something that I really want to call attention to, I will use danger.

[14:08] Then on my blog, if I have an article that is a work-in-progress that I would like to share with the world, but want to acknowledge is still within development, I will label that as a garden host and have some type of callout that indicates that.

[14:26] I recently started going through a book and showing my notes, so I thought it would be cool to actually have the more traditional quotes stand out a bit more from the other callouts. I added a book variant as well. I just want to flip back to the visuals of what this called in.

[14:49] It looked like again since we just saw the JSX, visually this is how the different variants differ from each other. For each variation, there are different styles particularly for borderlines, background color, etc. In the component, there is also some additional logic to add CSS class names based on the variant prop.

[15:18] This is the callout component. You'll see that I'm importing modular CSS from an external file. That has pseudo classes where I'm adding those emojis that go before each of the callouts. I am also passing in the class names from the variant prop.

[15:42] It had pulled that into this classes variable, which then passed in as a class name. Then the module CSS is able to appropriately target the callout based on the variant that it is a part of. Then there's also an abbreviation of the variant styles that were referenced in the previous slide.

[16:10] Another area of my website that I ended up updating as a reminder to MDX was I switched from Gatsby Remark PrismJS to prism react renderer in order to have more robust functionality that is available with react MDX.

[16:27] I found that PrismJS's sensibility was limited with the Gatsby Remark PrismJS plugin. In terms of being able to fully support the functionality of PrismJS, there are some plugins within PrismJS, such as the ability to copy the clipboard.

[16:50] When I was migrating, those were actually in development as separate Gatsby plugins. I decided why not just switch. I created a new code component for Prism React renderer to go through each line of an e-code snippet that I have within MDX.

[17:12] The components that are parsed into the MDX provider, there is this pre-code block, which allows us to replace the pre-markup with our parsing component and also retains any props that were parsed in the MDX. You'll see that I'm now importing this pre to code block from MDX util, as well as the code component that we just created.

[17:40] In terms of actually finishing off this migration after installing the necessary dependencies, creating the code component etc., there were some additional syntax changes that needed to be made in order to make sure that all of the code blocks were rendering how I intended for them to.

[18:01] I manually search through the different code samples that I had on my site to make sure that the code samples rendered properly. Mostly, that was adjusting some of the syntax for how lines that were highlighted were annotated, since there were differences between that syntax.

[18:22] Based off of the regex pattern and such, it was very manageable for me to do it on my particular site. In summary, I would definitely say that using MDX has greatly improved my developer experience. Swapping from Gatsby transformer Remark to Gatsby plugin MDX was pretty straightforward.

[18:43] I would say beware of potential plugin conflicts when you have multiple things that are manipulating the abstract syntax tree. That is just something to be aware of. I found that with MDX, it's a lot easier to customize posts. As I said, it has greatly improved the developer experience.

[19:02] I think one of the most obvious examples of this is if you look at the div of when I added the table of contents component. Not having to manually write out more than one line if I want to add a table of contents to a blog post has been a nice bonus.

[19:23] Now I want to share some resources that I found helpful. The first post is about how to create a table of contents in MDX. I thought it was especially interesting that they walk through how to recursively render a table of contents, which I think it's helpful if you want a component that is very robust.

[19:45] Of course, Prince's article on adding callouts, which inspired how I implemented them. It was a really great walkthrough. Chris wrote an article about code blocks and MDX-utils, which I think is helpful if you are interested in and working with MDX code blocks in your content.

[20:06] Some resources that I found helpful within MDX are the Gatsby-MDX-embed plugin, which I use to embed the code pin in the Twitter at the beginning of this talk. MDX-util, which was very essential to the code blocks. Then Prism React renderer, which I ended up using for the code blocks as well.

[20:26] In this presentation, I use a resource, Next MDX deck. The callouts that you saw in this presentation were actually MDX. Everything was run in MDX. Check them out if you're interested in creating a presentation in MDX.

[20:47] Thank you for having me. It's on you to go explore the following MDX. I want to look into creating interactive code examples on where you can actually manipulate the React code and see what happens. I also was considering creating a dynamic countdown component.

[21:07] If I had a speaking engagement or a big launch, having somewhere where people could see a live countdown and there would also be some type of markdown content on the page, is something that I'm also interested in exploring in the future.