Wanna see something cool? Check out Angular Spotify 🎧

Adding "loading=lazy" to GIF files on my 7-year-old Gatsby blog (with ChatGPT assistance)

My blog was built a long time ago using Gatsby. I vaguely remember cloning it from a template, but I can’t recall the exact name.

Over the years, I have added various features, such as the talks page, as I started giving talks.

The blog was initially built with Gatsby 1 and has been upgraded to Gatsby 2, where it has remained ever since. Although Gatsby 2’s support ended in 2021, I have procrastinated upgrading to the latest version, Gatsby 5. Since my blog is still functional and allows me to add new articles and talks, I haven’t felt a strong urge to perform a complete revamp or refactoring. It has become somewhat of a legacy system that continues to serve its purpose.

Here is the current package.json:

"dependencies": {
  "@sentry/gatsby": "^5.29.2",
  "bluebird": "^3.5.1",
+ "gatsby": "2.32.13",
  "gatsby-plugin-canonical-urls": "2.9.0",
  "gatsby-plugin-catch-links": "^2.0.4",
  "gatsby-plugin-feed": "^2.0.8",
  "gatsby-plugin-google-analytics": "^2.0.6",
  "gatsby-plugin-google-fonts": "latest",
  "gatsby-plugin-next-seo": "^1.6.1",
  "gatsby-plugin-offline": "^2.0.6",
  "gatsby-plugin-react-helmet": "^3.0.0",
  "gatsby-plugin-sass": "^2.0.1",
  "gatsby-plugin-sharp": "^3.15.0",
  "gatsby-plugin-sitemap": "^2.0.1",
  "gatsby-remark-autolink-headers": "^2.1.17",
  "gatsby-remark-copy-linked-files": "^2.0.5",
+ "gatsby-remark-images": "^6.12.1",
  "gatsby-remark-prismjs": "^3.0.2",

Perhaps one day, I will migrate the blog to Astro, but that remains a future endeavor. As a first step, I plan to migrate all my markdown files to a Notion database and connect my blog to fetch content from Notion instead of using the current markdown files. This change alone would be quite helpful. So, what’s the issue at hand?

In a recent article, I highlighted the awesomeness of the loading='lazy' property for images. I want to ensure that my blog also benefits from this feature. I recall adding the loading: 'lazy' option to gatsby-remark-images, which should apply the loading='lazy' attribute to all images by default.

{
  resolve: 'gatsby-transformer-remark',
  options: {
    tableOfContents: {
      maxDepth: 4,
    },
    plugins: [
      'gatsby-remark-autolink-headers',
      'gatsby-remark-lazy-gif',
      {
        resolve: 'gatsby-remark-images',
        options: {
          maxWidth: 960,
+         loading: 'lazy',
          linkImagesToOriginal: true,
        },

However, upon reviewing my latest article, I noticed that the GIF files do not have the loading='lazy' property. Consequently, when visiting the page, the GIF files load immediately, resulting in a significant amount of data being loaded, especially for larger GIF files. Since many of my blog posts include GIF files to showcase visual content, this issue needs to be addressed. For example, the Angular v17’s View Transitions: Navigate in Elegance contains several GIF files and at the moment, when you enter the page, all 12 GIF files are loaded immediately, costing about 70mb of data.

Adding "loading=lazy" to GIF files on my 7-year-old Gatsby blog

Current Setup

While I have configured gatsby-remark-images to include the loading='lazy' property by default for most images, GIF files are not processed by this plugin. For example, the following JPG file has the loading='lazy' property:

Adding "loading=lazy" to GIF files on my 7-year-old Gatsby blog

But the GIF file does not have the loading='lazy' property. The img tag below only has the src and alt attributes:

Adding "loading=lazy" to GIF files on my 7-year-old Gatsby blog

<img src="/0a94a1f59b4ffa9c061154716bd42de2/angular-view-transitions-07.gif" alt="Customised Transition">

The reason why GIFs are excluded can be found in the gatsby-remark-images documentation and some related issues:

Adding "loading=lazy" to GIF files on my 7-year-old Gatsby blog

Approach

What I want is simple: adding the loading='lazy' property to GIF files. Since gatsby-remark-images doesn’t support GIFs, I need to find an alternative solution.

- <img src="/0a94a1f59b4ffa9c061154716bd42de2/angular-view-transitions-07.gif" alt="Customised Transition">
+ <img src="/0a94a1f59b4ffa9c061154716bd42de2/angular-view-transitions-07.gif" alt="Customised Transition" loading="lazy">

Searching Google doesn’t yield any straightforward solutions.

Adding "loading=lazy" to GIF files on my 7-year-old Gatsby blog

Thus, I decided to ask ChatGPT for help. Here is the conversation:

See the full conversation here.

Adding "loading=lazy" to GIF files on my 7-year-old Gatsby blog

Attempt 1: GPT suggests updating gatsby-config.js for gatsby-remark-images

Basically, ChatGPT suggested updating the gatsby-config.js that utilise wrapperStyle from gatsby-remark-images. This custom transformer will add the loading="lazy" attribute to .gif images.

// gatsby-config.js

module.exports = {
  siteMetadata: {
    // Your site metadata
  },
  plugins: [
    {
      resolve: 'gatsby-transformer-remark',
      options: {
        plugins: [
          {
            resolve: 'gatsby-remark-images',
            options: {
              maxWidth: 800,
              loading: 'lazy', // Default loading for images
+              wrapperStyle: (fluidResult) =>
+                fluidResult.src.endsWith('.gif') ? 'loading="lazy"' : '',
+            },
          },
        ],
      },
    },
    // Other plugins
  ],
}

However, as gatsby-remark-images doesn’t support GIFs, this approach won’t work because the wrapperStyle function is only applied to images processed by gatsby-remark-images. GIFs are not processed by this plugin.

So I asked for a follow-up, and ChatGPT suggested writing a custom plugin to handle GIFs.

Attempt 2: Write a custom plugin to handle GIFs

Adding "loading=lazy" to GIF files on my 7-year-old Gatsby blog

Below are the steps suggested by ChatGPT:

If gatsby-remark-images is filtering out .gif images and you need to add loading="lazy" to them, you can use a custom plugin to directly modify the markdown AST before gatsby-remark-images processes it. Here’s how you can achieve this:

  1. Create the custom plugin directory structure:
mkdir -p plugins/gatsby-remark-lazy-gif
  1. Inside gatsby-remark-lazy-gif, create an index.js file with the following content:
// plugins/gatsby-remark-lazy-gif/index.js

const visit = require('unist-util-visit');

module.exports = ({ markdownAST }) => {
  visit(markdownAST, 'image', node => {
    if (node.url.endsWith('.gif')) {
      node.data = node.data || {};
      node.data.hProperties = node.data.hProperties || {};
      node.data.hProperties.loading = 'lazy';
    }
  });
  return markdownAST;
};
  1. Update your gatsby-config.js to use the custom plugin before gatsby-remark-images:
// gatsby-config.js

module.exports = {
  siteMetadata: {
    // Your site metadata
  },
  plugins: [
    {
      resolve: 'gatsby-transformer-remark',
      options: {
        plugins: [
+         'gatsby-remark-lazy-gif', // Add the custom plugin here
          {
            resolve: 'gatsby-remark-images',
            options: {
              maxWidth: 800,
            },
          },

I followed the instructions and created the custom plugin. Then, I updated the gatsby-config.js to include the custom plugin before gatsby-remark-images. However, it threw an error:

Plugin gatsby-remark-lazy-gif requires a package.json file

Adding "loading=lazy" to GIF files on my 7-year-old Gatsby blog

I asked for further guidance, and ChatGPT suggested creating a package.json file for the plugin, as seen in the conversation below:

Adding "loading=lazy" to GIF files on my 7-year-old Gatsby blog

And voila! It works! Now all my GIF files have the loading='lazy' property. As you scroll down the page, the GIF files are loaded dynamically, reducing the initial data load.

Adding "loading=lazy" to GIF files on my 7-year-old Gatsby blog

Conclusion

To boost website performance, add loading='lazy' to the img tag. While Gatsby’s default image transformer, gatsby-remark-images, doesn’t support GIFs, we can create a custom plugin to handle them.

ChatGPT has been super helpful in creating the plugin and solving this problem. I am a daily use of AI tools such as Copilot, ChatGPT and others and I really appreciate the value they bring to my work. This article was also written with ChatGPT’s help for grammar and suggestions.

Published 23 Jun 2024

    Read more

     — Angular v17’s View Transitions: Navigate in Elegance
     — Improving Website Performance with Browser-Level Image Lazy Loading (simply add loading="lazy")
     — Enhancing Cross-Document Navigation with the View Transitions API
     — Upgrading from Angular 15 to 17 in Nx Workspace: A Comprehensive Guide
     — How to change Visual Studio Code terminal font?

    Follow @trungvose on Twitter for more!