GatsbyJS Markdown Plugin: Automatically Open External Links In A New Tab
I’ve been doing a little research into how I can make posts written in markdown more suited for my needs and decided to use this opportunity to develop my own Gatsby Markdown plugin. Ever since I moved to Gatsby, making my own Markdown plugin has been on my todo list as I wanted to see how I could render slightly different HTML markup based on the requirements of my blog post content.
As this is my first markdown plugin, I thought it best to start small and tackle bug-bear of mine - making external links automatically open in a new window. From what I have looked online, some have suggested to just add an HTML anchor tag to the markdown as you will then have the ability to apply all attributes you’d want - including target
. I’ll quote my previous post about aligning images in markdown and why I’m not keen on mixing HTML with markdown:
HTML can be mingled alongside the markdown syntax... I wouldn't recommend this from a maintainability perspective. Markdown is platform-agnostic so your content is not tied to a specific platform. By adding HTML to markdown, you're instantly sacrificing the portability of your content.
Setup
We will need to create a local Gatsby plugin, which I’ve named gatsby-remark-auto-link-new-window
. Ugly name... maybe you can come up with something more imaginative. :-)
To register this to your Gatsby project, you will need start of with the following:
- Creating a plugin folder at the root of your project (if one hasn’t been created already).
- Add a new folder based on the name of our plugin, in this case - /plugins/gatsby-remark-auto-link-new-window.
- Every plugin consists of two files:
- index.js - where the plugin code to carry out our functionality will reside.
- package.json - contains the details of our plugin, such as name, description, dependencies etc. For the moment this can just contain an empty JSON object
{}
. If we were to publish our plugin, this will need to be completed in its entirety.
Now that we have our bare-bones structure, we need to register our local plugin by adding a reference to the gatsby-config.js
file. Since this is a plugin to do with transforming markdown, the reference will be added inside the 'gatsby-transformer-remark
options:
{
// ...
resolve: 'gatsby-transformer-remark',
options: {
plugins: [
{
resolve: 'gatsby-remark-embed-gist',
options: {
username: 'SurinderBhomra',
},
},
{
resolve: 'gatsby-remark-auto-link-new-window',
options: {},
},
'gatsby-remark-prismjs',
],
},
// ...
}
For the moment, I’ve left the options
field empty as we currently have no settings to pass to our plugin. This is something I will show in another blog post.
To make sure we have registered our plugin with no errors, we need run our build using the gatsby clean && gatsby develop
command. This command will always need to be run after every change made to the plugin. By adding gatsby clean
, we ensure the build clears out all the previously built files prior to the next build process.
Rewriting Links In Markdown
As the plugin is relatively straight-forward, let’s go straight into the code that will be added to our index.js file.
const visit = require("unist-util-visit");
module.exports = ({ markdownAST }) => {
visit(markdownAST, "link", (node) => {
if (node.url.startsWith("http")) {
node.data = node.data || {};
node.data.hProperties = node.data.hProperties || {};
node.data.hProperties.target = "_blank";
node.data.hProperties.rel = "noopener noreferrer";
}
});
return markdownAST;
};
As you can see, I want to target all markdown link nodes and depending on the contents of the url
property we will perform a custom transformation. If the url
property starts with an "http" we will then add a new attribute, "target" using hProperties
. hProperties
refers to the HTML attributes of the targeted node.
To see the changes take effect, we will need to re-run gatsby clean && gatsby develop
.
Now that we have understood the basics, we can beef up our plugin by adding some more functionality, such as plugin options. But that's for another post.