Pre-Loading External Images in Gatsby JS
Tags: gatsby, react, javascript
Published: 2020-04-18 23:33:22

Gatsby is a great tool that lets you build awesome applications! In fact, I use Gatsby for my own site (hopefully you're reading this on there 😃), but I ran into an issue with loading externally hosted images. The issue was that since the images were externally hosted I had to fetch them on each page load and some images were optimized and thus it would take longer and affect the user experience.

I heard great things about the [Gatsby Image ](<https://www.npmjs.com/package/gatsby-image>) library specifically for solving image optimization issues but didn't know how to use it for externally hosted images since my blog is powered by [Ghost CMS](<https://ghost.org/>). Luckily I managed to figure it out so this is a guide to walk you through what I did.

### Installing Dependencies ⚙️

You're only going to need 3 dependencies, most of which you should already have, but just in case you don't, you're going to need the following:

[gatsby-source-filesystem](<https://www.npmjs.com/package/gatsby-source-filesystem>) \-> We're going to use a function this provides to download files from a remote source, in this case, images. **Import the `createRemoteFileNode` function from this in your file.**

[gatsby-plugin-sharp](<https://www.npmjs.com/package/gatsby-plugin-sharp>) \-> This will be used to generate the image object which is then ingested by gatsby-image. **Import the `fluid` function from this in your file.**

[gatsby-image](<https://www.npmjs.com/package/gatsby-image>) \-> This provides the `Img` component that will be used in our React code

### Implementation 👨‍💻

Most of the work will be done in the `gatsby-node.js` file, you can choose to do this in the `createPages` or `createNode` exported functions, I did it in the `createPages` just because I was running into some issues with my pagination plugin when attempting to do this in the `createNode`.

The `createPages` function is passed a param which has multiple functions, if destructuring the param make sure to destructure the following. You can also further destructure from the actions param:

<!--kg-card-begin: markdown-->

```javascript
exports.createPages = async ({ actions, getCache, createNodeId, cache, reporter }) => {
const { createPage, createNode } = actions; 
...
```

<!--kg-card-end: markdown-->

I'm going to assume that you already have the URL(s) for the image(s) stored in some variable that you will pass to a function that will generate all of these images for you.

Firstly you'll have to create a fileNode which is essentially downloading the file (image) from a remote URL and storing that as an object. That would look like this:

<!--kg-card-begin: markdown-->

```javascript
const fileNode = await createRemoteFileNode({
    url: image_url,
    parentNodeId: id,
    getCache,
    createNode,
    createNodeId,
  });
```

<!--kg-card-end: markdown-->

Now that you have "downloaded" the remote file (image) you'll now need to generate an image object from it using `gatsby-plugin-sharp`. That would look like this:

<!--kg-card-begin: markdown-->

```javascript
const generatedImage = await fluid({
    file: fileNode,
    reporter,
    cache,
  });
```

<!--kg-card-end: markdown-->

Now you have your generated image, and you can pass that to whatever page you want. Below you'll find a snippet from my implementation, and you'll see how that is passed to my posts pages. I'm using a map to store each generated image and to prevent duplicates, plus it makes retrieval of each image object easier for me. I then only pass the generated image for each specific post.

<!--kg-card-begin: markdown-->

```javascript
// This is the function that does all the work
  const generateImages = async (pages) => {
    const featureImages = new Map();

    for (const page of pages) {
      const { node } = page;

      if (featureImages.has(node.slug)) {
        return;
      }

      const fileNode = await createRemoteFileNode({
        url: node.image_url,
        parentNodeId: node.id,
        getCache,
        createNode,
        createNodeId,
      });

      const generatedImage = await fluid({
        file: fileNode,
        reporter,
        cache,
      });

      featureImages.set(node.slug, generatedImage);
    }

    return featureImages;
  };

  const fluidImages = await generateImages(posts);

  // Create post pages
  posts.forEach(({ node }) => {
    createPage({
      path: node.url,
      component: postTemplate,
      context: {
        slug: node.slug,
        fluidImage: fluidImages.get(node.slug),
      },
    });
  });
```

<!--kg-card-end: markdown-->

---

So now that we have the `gatsby-plugin-sharp` fluid images and have them passed to the `postTemplate` page the usage is quite simple. Note that the `fluidImage` is accessible via the props specifically under `props.pageContext.fluidImage`.

Firstly you want to import the following at the top of your `postTemplate` or whatever component page.

<!--kg-card-begin: markdown-->

```javascript
import Img from 'gatsby-image';
```

<!--kg-card-end: markdown-->

Now in your JSX simple use the `Img` component and access the passed `fluidImage`, it would look something like the following:

<!--kg-card-begin: markdown-->

```html
<Img fluid={props.pageContext.fluidImage} />
```

<!--kg-card-end: markdown-->

## You're all done!! 🎉🎉🎉

Yay, you're all done, now that you know how to generate the image and use it, you can now go back and read the docs for the various libraries to figure out how to configure them for optimal performance for your use case!

> Live Long and Program 🖖👨‍💻

> This post was originally posted on my personal site, check it out [here](https://ameersami.com/blog/pre-loading-external-images-in-gatsby-js/)