July 29, 2022 Author • Paul Scanlon

How to use Gatsby's Head API with MDX

  • Gatsby
  • React
  • JavaScript
  • MDX

Hi there! I'm excited, are you? Of course you are!

In Gatsby 4.19.0 the team shipped the Gatsby Head API 🎉

But what does this mean for you, and why am I excited?

React Helmet

Historically the way to add indexable meta data to you Gatsby site's <head> element was to use a combination of react-helmet and gatsby-plugin-react-helmet, but rather worryingly, react-helmet hasn't really been updated since 2020. 😬

What does it mean for you?

Using an Open-source library that's not been well maintained can lead to headaches, as I'm sure you're well aware.

Why Am I excited?

The Gatsby Engineering team recognizes this and have now moved all of that lovely Helmet functionality into the core framework! — Superb!

Migration Options

To use the Head API today, upgrade to at least 4.19.0 and I'll now talk you through the steps required to migrate from react-helmet to the Head API. There's two slightly different ways you might wish to approach this depending on if you're using unique pages or template/layout file. (MDX Blog posts with frontmatter for example)

I've prepared an example repo and x2 PR's which you can use for reference.

Example Repo (Using React Helmet)

PR's (Using The Head API)

Getting Started

I've tried to consider the most common scenario based on the approaches I see many folks use. Your use case may well be different.

Remove React Helmet

npm uninstall react-helmet gatsby-plugin-react-helmet
// gatsby-config.js
module.exports = {
...
plugins: [
- 'gatsby-plugin-react-helmet',
...
]
};

Page

Generally I see folks using an <Seo /> component somewhere in a page or page template file. In the example repo please have a look at src/pages/index.js#L9, and here's a similar looking code snippet.

Seo

// src/pages/index.js
import React, { Fragment } from 'react';
import Seo from '../components/seo';
const Page = () => {
return (
<Fragment>
<Seo title="Gatsby Head API MDX" />
<main>...</main>
</Fragment>
);
};
export default Page;

... and here's what the same page looks like using the Head API

export const Head

// src/pages/index.js
import React, { Fragment } from 'react';
import Seo from '../components/seo';
const Page = () => {
return (
<Fragment>
- <Seo title="Gatsby Head API MDX" />
<main>...</main>
</Fragment>
);
};
export default Page;
+ export const Head = () => {
+ return <Seo title="Gatsby Head API MDX" />;
+ };

Seo component

Now you can remove any reference to <Helmet /> from the <Seo /> component.

// src/components/seo.js
import React from 'react';
- import { Helmet } from 'react-helmet';
const Seo = ({ title }) => {
return (
- <Helmet>
<title>{title}</title>
- </Helmet>
);
};
export default Seo;

... and that's it!

Frontmatter as title

The above example shows a simple method for "hard-coding" a title and passing it on to the <Seo /> component via the title prop. In Page templates you'll likely need to use the title as defined in the frontmatter. Take a look at the src from the example repo: src/pages/posts/{mdx.frontmatter__title}.js#L43

Head props

Before you get going, you might like to inspect the props passed to the Head API. They should be the same as what's passed to the page, E.g.

export const Head = (props) => {
console.log(JSON.stringify(props, null, 2));
return null;
};

In my example repo this results in something similar to the below.

{
"location": {
"pathname": "/posts/this-is-post-one"
},
"params": {},
"data": {
"mdx": {
"frontmatter": {
"title": "This is post one"
},
"body": "..."
}
},
"pageContext": {
"id": "6aa907b2-4040-5e38-b6f0-4f1762068476"
}
}

The bit I'm most interested in is data.mdx.frontmatter.title as this is what I'll need to pass on to the <Seo /> component to display in the HTML <title />.

export const Head = ({
data: {
mdx: {
frontmatter: { title }
}
}
}) => {
return <Seo title={title} />;
};

Now when I visit each of the post pages in the browser I see the page title change in my browser tab, and when inspect the DOM I see the following. Notice: the data attribute on the title. If it says data-gatsby-head you're all set!

// http://www.mywebsite.com/post-one
<head>
<title data-gatsby-head="true">This is post one</title>
</head>

... and that's it, for real this time!

How am I doing?

Hey! Lemme know if you found this helpful by leaving a reaction.