By 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!

Further Reading

Hey!

Leave a reaction and let me know how I'm doing.

  • 0
  • 0
  • 0
  • 0
  • 0
Powered byNeon