MDX link routing in Gatsby

April 3, 2020

In my last article, I wrote about how I was able to parse frontmatter through MDX in Gatsby for a more powerful writing/developer experience. I also mentioned a few remark plugins I brought in-house. Today, I’ll walkthrough how I was able to remove three dependencies for just a few lines of code.

Creating links in Markdown is fairly straightfoward:

md
1[Link](https://www.zslabs.com)

With the help of a few plugins, we’re able to make these links do a bit more for us automagically:

With MDX’s ability to override normal DOM elements, I was able to encapsulate the logic from all three of these plugins into a handy snippet:

MarkdownLink.js
js
1import PropTypes from 'prop-types'
2import { Link as GatsbyLink } from 'gatsby'
3
4// Checks against absolute URLs that share 👇 so we can still pass it along to Gatsby's internal link component
5const domainRegex = /http[s]*:\/\/[www.]*YOURDOMAIN\.com[/]?/
6// @NOTE We can use a REGEX like this for URLs we want to be treated as external which could be used for Netlify redirects
7// /http[s]*:\/\/[www.]*YOURDOMAIN\.com(?!\/i-am-external|\/me-too)[/]?/
8
9const MarkdownLink = ({ href, ...rest }) => {
10 const sameDomain = domainRegex.test(href)
11
12 if (sameDomain) {
13 href = href.replace(domainRegex, '/')
14 }
15
16 if (href.startsWith('/')) {
17 return <GatsbyLink data-link-internal to={href} {...rest} />
18 }
19
20 // Treat urls that aren't web protocols as "normal" links
21 if (!href.startsWith('http')) {
22 return <a href={href} {...rest} /> // eslint-disable-line jsx-a11y/anchor-has-content
23 }
24
25 return (
26 <a
27 data-link-external
28 href={href}
29 target="_blank"
30 rel="noopener noreferrer nofollow"
31 {...rest}
32 />
33 )
34}
35
36MarkdownLink.propTypes = {
37 href: PropTypes.string.isRequired,
38}
39
40export default MarkdownLink

This can then be passed into <MDXProvider /> to override default links:

Article.js
js
1import { MDXProvider } from '@mdx-js/react'
2import { MDXRenderer } from 'gatsby-plugin-mdx'
3
4import MarkdownLink from '../components/MarkdownLink'
5
6const Article = ({ frontmatter, body }) => (
7 <MDXProvider
8 components={[
9 {
10 a: (props) => <MarkdownLink {...props} />,
11 },
12 ]}
13 >
14 <div>
15 <h1>{frontmatter.title}</h1>
16
17 <MDXRenderer>{body}</MDXRenderer>
18 </div>
19 </MDXProvider>
20)

Since our Markdown links are now powered by a React component, we can apply any other special formatting or logic as needed. Take a look at this one and others I’ve added to my own site in my shortcodes file. 🎉