How to add support for Gatsby ImageCDN in your source plugin  🖼 ☁️

On yesterday's unauthorized and rum-fueled treasure hunt in the sharky waters around the Gatsby islands, the great and powerful Ward Peeters helped us add Gatsby ImageCDN support to the YouTube oEmbed plugin.

Screengrab of stream

It was his third time on the show; if you missed the other two, have fun catching up:

Why add Gatsby ImageCDN support?

Ward said it best:

  • Faster builds (both local and in the cloud)
  • Better performance in the browser compared to a CDN on a different domain

Adding support for Gatsby ImageCDN still lets you deploy to other services like Netlify. Build time there will be faster, in the same way, it's faster locally, by only downloading the images in use by the site. The "old way" createRemoteFileNode would eagerly download all images.

How to add Gatsby ImageCDN support?

We'll need to replace the use of createRemoteFileNode with creating our own RemoteFile nodes.

Many CMSs already have their own asset node type they can extend with RemoteFile, but for our plugin, we first need to create a new node type that extends RemoteFile:

// gatsby-node.js
const {
} = require("gatsby-plugin-utils/polyfill-remote-file");

exports.createSchemaCustomization = ({ actions, schema }) => {
        name: `YouTubeThumbnail`,
        fields: {
          youTubeId: "String!",
        interfaces: [`Node`, `RemoteFile`],

addRemoteFilePolyfillInterface makes sure the plugin works even if the Gatsby site is on a lower version than 4. To make sure it works on older versions even in develop, you want to add polyfillImageServiceDevRoutes:

// gatsby-node.js
const {
} = require("gatsby-plugin-utils/polyfill-remote-file");

exports.onCreateDevServer = ({ app }) => {

Then we are ready to replace our usage of createRemoteFileNode in onCreateNode 🎉

exports.onCreateNode = async (gatsbyUtils) => {
  const { node, actions, reporter, createNodeId } = gatsbyUtils;
  const { createNodeField, createNode } = actions;

  if (node.internal.type === `YouTube`) {
    const youTubeThumbnailNodeId = createNodeId(

      id: youTubeThumbnailNodeId,
      youTubeId: node.youTubeId,
      url: node.oEmbed.thumbnail_url,
      mimeType: "image/jpeg",
      filename: node.youTubeId + ".jpg",
      height: node.oEmbed.thumbnail_height,
      width: node.oEmbed.thumbnail_width,
      internal: {
        type: `YouTubeThumbnail`,
        contentDigest: node.internal.contentDigest,

      name: `thumbnailFileId`,
      value: youTubeThumbnailNodeId,
      `Created YouTubeThumbnail Node for ${node.youTubeId} thumbnail`

The last step for us is to update the YouTube Node's schema customization to use the new node type YouTubeThumbnail instead of File:

// gatsby-node.js
    type YouTube implements Node {
      thumbnail: YouTubeThumbnail @link(from: "youTubeId" by: "youTubeId")

Check out the Pull Request on GitHub for the complete diff!

All the best,
Queen Raae

Stuck on a reef in the sharky waters around the Gatsby islands?

Book a 1-on-1 Gatsby Call with Queen Raae. Friendly advice you can put into action immediately, guaranteed!

Serious about Gatsby? Sign up for emails sent every weekday to help you get the most out of Gatsby!