May 24, 2019

Using Gatsby Image with Netlify CMS

gatsbynetlifyCMStipstutorialJAMstack

Why is nothing ever simple??

Gatsby is awesome. Netlify CMS is good. But getting them to play nicely together can be a bumpy road.

One of GatsbyJS's main selling points is it's excellent image optimisation. Gatsby Image is a react component that does all the hard work of image optimisation for you. You feed it an image and it resizes to the optimal size and even provides a beautiful 'blur up' effect while the image loads. This is particularly handy if you have a stonking great hero image across your homepage that needs to be high res.

Adding gatsby-image to your site.

This is relatively simple but it does require you to pull in the image using GraphQL.

Have a read of Gatsby's own page to learn how to add gatsby-image to your site.

Querying images in markdown.

Okay, so we know how to add gatsby-image to our website when we have the image stored in a folder and we query it directly. But how do we query it if it is referenced in a markdown file?

Enter gatsby-remark-images. Install this badboy:

npm install --save gatsby-remark-images

We need to put it in the gatsby-config.js file now. It has to be entered as a plugin of gatsby-transformer-remark and you should enter a maximum width that you would like the images to be.

Make sure you also have a source-instance for your markdown files.

Note: We are storing the image in the same folder as the markdown file.

// In your gatsby-config.js file

module.exports = {
    plugins: [
        // ... other plugins
        {
            resolve: `gatsby-source-filesystem`,
                options: {
                    path: `${__dirname}/blog`,
                    name: 'blog-posts',
                },
        },
        {
            resolve: `gatsby-transformer-remark`,
            options: {
                plugins: [
                    {
                        resolve: `gatsby-remark-images`,
                        options: {
                            maxWidth: 590,
                        },
                    },
                ],
            },
        },
        // ... other plugins
    ],
}

Let's say we have a markdown file that looks like this:

---
title: 'My first blog post'
date: 2019-05-24T11:55:00.000Z
---
# Welcome to my first blog post.

Hello, this is a blog post with a nice picture of a dog.

![A picture of a cute dog](./cutedog.jpg)

With gatsby-remark-images installed and setup this picture will automatically have all the benefits of gatsby-image. Including a lovely blur up effect!

But why does this not work with Netlify CMS?

If you've tried to implement this method when using Netlify CMS you probably ran into an issue where you got this error:

GraphQL Error Field "image" must not have a selection since type "String" has no subfields.

The reason for this is straightforward (but frustrating!).

In the example above, the image was stored in the same folder as the markdown file and it was referenced using a relative path.

However... Netlify CMS stores it's images in a separate folder and when you add an image tag using their markdown editor or using their image widget it writes it like this:

![A picture of a cute dog](/cutedog.jpg)
---
thumbnail: /cutedog.jpg
---

That's not a relative path! And its certainly not going to take us all the way to static/assets/cutedog.jpg which is the default media storage location for Netlify CMS.

The result is GraphQL can't find the image and therefore turns it into a string instead of an object. When graphQL tries to go into the image object to get the fancy image processing stuff it errors because it's a string.

The fix for this is to use a relative path. Well, that's easy enough if you're handcoding your markdown files. But how do you get Netlify to do this?

Enter gatsby-remark-relative-images

This beautiful package was built specifically to tackle this issue with Netlify CMS. It transforms all the paths in your markdown files to relative paths, making everything work like a dream.

npm install --save gatsby-remark-relative-images

Add it to your gatsby-config.js file. It must be before gatsby-remark-images.

Make sure you've got a source instance set up for the images folder created by Netlify CMS too. This must be before other source instances too!

// In your gatsby-config.js file

module.exports = {
    plugins: [
        // ... other plugins

        {            resolve: `gatsby-source-filesystem`,                options: {                    path: `${__dirname}/static/assets`,                    name: 'images',                },        },        {
            resolve: `gatsby-source-filesystem`,
                options: {
                    path: `${__dirname}/blog`,
                    name: 'blog-posts',
                },
        },
        {
            resolve: `gatsby-transformer-remark`,
            options: {
                plugins: [
                    'gatsby-remark-relative-images',                    {
                        resolve: `gatsby-remark-images`,
                        options: {
                            maxWidth: 590,
                        },
                    },
                ],
            },
        },
        // ... other plugins
    ],
}

This will now work for images referenced in markdown. e.g. ![A picture of a cute dog](/cutedog.jpg).

Frontmatter images.

If we also want it to work for images referenced in the frontmatter e.g. thumbnail: /cutedog.jpg we need to do one more step.

Open up your gatsby-node.js file if you have one. If you don't already have one, make one in the root.

If this file is empty or does not already have a exports.onCreateNode function in it add the following code:

// In your gatsby-node.js file

const { fmImagesToRelative } = require('gatsby-remark-relative-images');

exports.onCreateNode = ({ node }) => {
  fmImagesToRelative(node);
};

You may already have some code in this file if you programmatically generate blog pages and their slugs for example. If this is the case you need to put the function in the existing code and not just copy/paste the whole thing in!

Here is an example of how to integrate the code if you're already generating slugs:

// In your gatsby-node.js file

const { fmImagesToRelative } = require('gatsby-remark-relative-images');
exports.onCreateNode = ({ node, actions, getNode }) => {
  const { createNodeField } = actions
  fmImagesToRelative(node)  if (node.internal.type === `MarkdownRemark`) {
    const value = createFilePath({ node, getNode })
    createNodeField({
      name: `slug`,
      node,
      value,
    })
  }
}

Hopefully this fix will work as well for you as it did for me!