A blog about programming, Web development, Open Source, Linux and DevOps.

Open Links in a New Tab on your Gatsby MDX Blog

If you prefer to open all the external links on your Gatsby MDX powered blog in a new tab, here’s how to do it without installing any plugins.

1. Create a component to replace the a element

When using MDX you can replace the default HTML elements that Markdown compiles to with your own components , so we can replace the a element with our own custom Link component.

Here’s an example component that wraps Gatsby’s Link component and uses it only for internal links, and adds target="_blank" for the external links:

1import React from 'react';
2import { Link as GatsbyLink } from 'gatsby';
4export default function Link({ children, href }) {
5  if (href.startsWith('/')) {
6    // Use Gatsby's Link component for internal site navigation
7    // to benefit from the preloading features
8    // See:
9    return <GatsbyLink to={href}>{children}</GatsbyLink>;
10  }
12  // Check if the link is for a section on the page
13  // We don't want to add the attributes for the on page links
14  const onPage = href.startsWith('#');
16  return (
17    <a
18      href={href}
19      // Open the link in a new page
20      target={onPage ? null : '_blank'}
21      // Add noopener and noreferrer for security reasons
22      rel={onPage ? null : 'noopener noreferrer'}
23    >
24      {children}
25    </a>
26  );

The rel attribute is very important, here are some notes from MDN :

Note: When using target, add rel="noreferrer noopener" to avoid exploitation of the window.opener API;

Note: Linking to another page with target="_blank" will run the new page in the same process as your page. If the new page executes JavaScript, your page’s performance may suffer. This can also be avoided by using rel="noreferrer noopener".

SEO Note:

If you want the same result as the gatsby-remark-external-links plugin, you need to also add nofollow.

Usually, you want to do this if you don’t have control over the posted content (eg: user-generated content), but if you’re adding this to your blog, you can safely ignore it because you won’t link to sites that you don’t endorse in the first place and if you link to sites usually you want them to benefit from this link.

2. Replace the a elements with the new component

Now you can replace the a elements in your MDX files with this new Link component which can be done using MDXProvider (uses the React’s Context API ), the idea is to wrap the MDXRenderer component.

You can do this in your Layout component:

1import React from 'react';
2import { MDXProvider } from '@mdx-js/react';
3import Link from './Link';
5export default function Layout({ children }) {
6  return (
7    <MDXProvider
8      components={{
9        // Map the HTML elements to React components
10        a: Link,
11      }}
12    >
13      {children}
14    </MDXProvider>
15  );

Or directly where you’re using the MDXRenderer component:

1import React from 'react';
2import { MDXRenderer } from 'gatsby-plugin-mdx';
3import { MDXProvider } from '@mdx-js/react';
4import Link from './Link';
6export default function BlogPost({ data }) {
7  const { body } = data.mdx;
9  return (
10    <MDXProvider
11      components={{
12        // Map the HTML elements to React components
13        a: Link,
14      }}
15    >
16      <MDXRenderer>{body}</MDXRenderer>
17    </MDXProvider>
18  );

That’s it, now all the links in your MDX files will be using this new Link component and the external links will open in a new tab.