© Joe Attardi 2020
J. AttardiUsing Gatsby and Netlify CMShttps://doi.org/10.1007/978-1-4842-6297-9_11

11. Working with Images

Joe Attardi1 
(1)
Billerica, MA, USA
 

In Chapter 9, we added a hero image to the index page. If you got the image from Unsplash, as recommended, you might notice that the image file is very large. This could result in slow loading times for some users of the site.

Gatsby has plugins that work with an image processing library called Sharp (https://sharp.pixelplumbing.com/) to resize and crop images in various ways at build time. This can be combined with the gatsby-image plugin to create a powerful image processing pipeline.

Using these plugins also provides two effects to show “placeholder” images as the full image is loading:
  • Blur-up: Creates an extremely small size of the image that will load very quickly and sets that as the image, blurred, while the full image loads

  • Traced placeholder: Creates an SVG showing the outline of the full image that is displayed while the full image loads

In this chapter, we’ll use these plugins to optimize the hero image on the index page.

Plugins

To accomplish this task, we’ll require several dependencies:
  • gatsby-plugin-sharp : Low-level plugin that provides integration with the Sharp library.

  • gatsby-transformer-sharp : Utilizes gatsby-plugin-sharp to add additional image data to the GraphQL schema that can be used by the gatsby-background-image plugin to display optimized or processed images.

  • gatsby-remark-relative-images : Used to convert image paths to relative paths so that they can be matched by gatsby-remark-images. This is a plugin for gatsby-transformer-remark.

  • gatsby-remark-images : Exposes images in Markdown front matter so that they can be processed by Sharp. This is also a plugin for gatsby-transformer-remark.

  • gatsby-background-image : Provides a BackgroundImage component that is used for creating a container element with a background image that utilizes the data from gatsby-transformer-sharp. This is not a plugin but just a component that we will import into our page.

Let’s go to the project directory in a terminal and install these new dependencies:
npm install [email protected]
            [email protected]
            [email protected]
            [email protected]
            [email protected]

Adding the plugins to the Gatsby configuration

Now that the plugins are installed, we need to add them to the Gatsby configuration file. Open the file gatsby-config.js and update it with the code from Listing 11-1.
module.exports = {
  siteMetadata: {
    title: 'The Coffee Blog'
  },
  plugins: [
    'gatsby-plugin-netlify-cms',
    {
      resolve: 'gatsby-source-filesystem',
      options: {
        name: 'images',
        path: 'static/img'
      }
    },
    {
      resolve: 'gatsby-source-filesystem',
      options: {
        name: 'blog',
        path: 'src/blog'
      }
    },
    {
      resolve: 'gatsby-source-filesystem',
      options: {
        name: 'pageData',
        path: 'src/pageData'
      }
    },
    {
      resolve: 'gatsby-transformer-remark',
      options: {
        plugins: [
          'gatsby-remark-relative-images',
          'gatsby-remark-images'
        ]
      }
    },
    'gatsby-plugin-sharp',
    'gatsby-transformer-sharp'
  ]
};
Listing 11-1

Updating the configuration file

First, we’ve added a new instance of the gatsby-source-filesystem plugin, to source the image data. This must be the first gatsby-source-filesystem instance in your configuration file. Sourcing the image files before the other content is necessary for the GraphQL schema to be correct. Otherwise, you may get an error when querying the image nodes.

Then, we added the two plugins to gatsby-transformer-remark . Previously, the plugin entry for this plugin was a simple string. We have changed it to be an object with a resolve and options property so that we can specify plugins for it. gatsby-remark-relative-images must come before gatsby-remark-images.

Lastly, we add the two other plugins at the end.

There’s one more step that’s needed for all of this to work. gatsby-remark-relative-images has a function called fmImagesToRelative that we need to call in order to adjust the image paths to relative paths. We do this in gatsby-node.js. Open this file, and add the code indicated in Listing 11-2.
const path = require('path');
const { createFilePath } = require('gatsby-source-filesystem');
const { fmImagesToRelative } = require('gatsby-remark-relative-images');
exports.onCreateNode = function({ node, getNode, actions }) {
  fmImagesToRelative(node);
  const { createNodeField } = actions;
  if (node.internal.type === 'MarkdownRemark') {
    const slug = createFilePath({ node, getNode });
    createNodeField({
      node,
      name: 'slug',
      value: slug
    });
  }
};
exports.createPages = async function({ graphql, actions }) {
  const { createPage } = actions;
  const result = await graphql(`
    query {
      allMarkdownRemark {
        edges {
          node {
            frontmatter {
              contentKey
            }
            fields {
              slug
            }
          }
        }
      }
    }
  `);
  const posts = result.data.allMarkdownRemark.edges
    .filter(edge => edge.node.frontmatter.contentKey === 'blog');
  posts
    .forEach(({ node }) => {
      createPage({
        path: node.fields.slug,
        component: path
          .resolve('src/templates/blog.js'),
        context: {
          slug: node.fields.slug
        }
      });
    });
    const pageSize = 5;
    const pageCount = Math.ceil(posts.length / pageSize);
    const templatePath = path.resolve('src/templates/blog-list.js');
    for (let i = 0; i < pageCount; i++) {
      let path = '/blog';
      if (i > 0) {
        path += `/${i + 1}`;
      };
      createPage({
        path,
        component: templatePath,
        context: {
          limit: pageSize,
          skip: i * pageSize,
          pageCount,
          currentPage: i + 1
        }
      });
    }
};
Listing 11-2

Updating gatsby-node.js

How gatsby-transformer-sharp works

There are a few different ways you can process images with Sharp for a Gatsby site.

Fixed images are generated at a specific width and height. A few images are generated for different pixel densities.

Fluid images are generated at different sizes for different screen sizes, and the appropriate image will be requested based on the container width.

You can also resize an image, specifying how it should be cropped.

Without this transformer plugin, the field for an image path is a simple string, as we’ve seen before. When we ran the query in Listing 11-3 on the index page, the heroImage field returned a string representing the path of the image.
{
  site {
    siteMetadata {
      title
    }
  }
  markdownRemark(frontmatter: { contentKey: { eq: "indexPage" } }) {
    frontmatter {
      tagline
      heroImage
    }
  }
}
Listing 11-3

The GraphQL query from the index page

Once we have added the transformer plugin, the heroImage field is no longer just a string. Instead, it is a node with many other fields attached to it, representing data about the image. It has fields for each representation of the image: fixed, fluid, and so on.

These fields are passed along to the BackgroundImage component that is given to us by the gatsby-background-image package. It has corresponding props such as fixed and fluid.

Each of the image fields in the GraphQL schema include many subfields which are expected to be passed into the BackgroundImage component. Instead of querying for each of these manually, the plugin defines several GraphQL fragments that we can add to our query instead.

GraphQL fragments

We’ve seen many examples of GraphQL queries. Listing 11-4 shows the query we use for the menu page, which selects several different fields at different levels .
{
  markdownRemark(frontmatter: { contentKey: { eq: "menu" } }) {
    frontmatter {
      title
      categories {
        name
        items {
          name
          description
          price
        }
      }
    }
  }
}
Listing 11-4

The query for the menu page

In this case, we are only querying for this data once. But suppose we wanted to query for this same data in several places. Using this approach, we would have to repeat this query everywhere. However, we can also create a GraphQL fragment that defines the fields we want to select, then reuse that fragment. Listing 11-5 shows the same query but using a fragment.
fragment MenuFields on MarkdownRemark {
  frontmatter {
    title
    categories {
      name
      items {
        name
        description
        price
      }
    }
  }
}
{
  markdownRemark(frontmatter: { contentKey: { eq: "menu" } }) {
    ...MenuFields
  }
}
Listing 11-5

The menu query using a fragment

Now, anywhere we query a MarkdownRemark node, we can reference the MenuFields fragment to query for all those fields.

gatsby-transformer-sharp gives us several predefined fragments that we can use in our queries. These fragments include all the fields needed by the BackgroundImage component. Some examples of these fragments are
  • GatsbyImageSharpFixed

  • GatsbyImageSharpFluid

These fragments can then be passed to the corresponding props on the BackgroundImage component to use the image data.

Using the BackgroundImage component

Now that we’ve installed and configured the proper plugins, let’s update the index page to use the BackgroundImage component. We’ll need to adjust the GraphQL query to include the image data, then change from using a plain div to the BackgroundImage component .

Open the file src/pages/index.js and update the code to match Listing 11-6.
import React from 'react';
import { graphql, useStaticQuery } from 'gatsby';
import BackgroundImage from 'gatsby-background-image';
import BlogList from '../components/BlogList';
import Layout from '../components/Layout';
import styles from './index.module.css';
export default function IndexPage() {
  const data = useStaticQuery(graphql`
    {
      site {
        siteMetadata {
          title
        }
      }
      markdownRemark(frontmatter: { contentKey: { eq: "indexPage" } }) {
        frontmatter {
          tagline
          heroImage {
            childImageSharp {
              fluid {
                ...GatsbyImageSharpFluid
              }
            }
          }
        }
      }
    }
  `);
  const tagline = data.markdownRemark.frontmatter.tagline;
  const heroImage = data.markdownRemark.frontmatter.heroImage;
  return (
    <Layout>
      <BackgroundImage
        id={styles.hero}
        fluid={heroImage.childImageSharp.fluid}>
        <h1>{tagline}</h1>
      </BackgroundImage>
      <BlogList />
    </Layout>
  );
}
Listing 11-6

Updating the index page

Previously, the GraphQL query was selecting the heroImage field from the front matter as a string and using that as the background image URL. Now that we have the gatsby-transformer-sharp plugin installed, heroImage is a node containing other fields with the image data. We select the childImageSharp field, then its fluid field, then finally we select the GatsbyImageSharpFluid fragment .

The BackgroundImage component has a fluid prop that expects the data from the GatsbyImageSharpFluid fragment. Similarly, it has a fixed prop that expects the data from the GatsbyImageSharpFixed fragment. Here, we want a fluid image, so we’re using the fluid prop.

After making these changes and reloading the index page, you may notice the “blur-up” effect in action. While the full-size image is loading, a low-resolution blurred version is shown as a placeholder which loads very quickly. You might have to open your browser’s developer tools and throttle the network connection to see this effect, shown in Figure 11-1.
../images/502348_1_En_11_Chapter/502348_1_En_11_Fig1_HTML.jpg
Figure 11-1

The “blur-up” effect

Disabling the “blur-up” effect

If you would rather not use the “blur-up” effect with an image, it’s easy to disable it. In your GraphQL query, instead of selecting the GatsbyImageSharpFluid fragment, you can select the GatsbyImageSharpFluid_noBase64. The low-resolution image used for the blur is specified as a Base64 string. This fragment disables the effect.

Fixing the header background

Our site has another very large image that it loads. The background of the header area shows a faded image of coffee beans. This is currently not optimized. The image being loaded has a size of 5.4 MB, at a large size of 6,000 pixels by 4,000 pixels. This will definitely have an effect on page performance.

We can utilize the BackgroundImage component and the Sharp image processing to improve this as well, in a few short steps.

Moving the image

Currently, the coffee beans image is located in the root of the static directory. Here, it won’t be seen by gatsby-source-filesystem. We configured gatsby-source-filesystem to look for files in the static/img directory. So, as a first step, go ahead and move static/coffee.jpg to static/img/coffee.jpg.

Once we move the file here, and restart the server, it will be picked up and added to the GraphQL schema.

Modifying the Layout component to use a static query

The next step is to modify the Layout component. Currently, it’s setting the background image of the header via CSS. We’ll have to make a GraphQL query for the background image, then pass the data to a BackgroundImage component.

Open the file src/components/Layout.js and make the changes shown in Listing 11-7.
import React from 'react';
import { Link, graphql, useStaticQuery } from 'gatsby';
import BackgroundImage from 'gatsby-background-image';
import styles from './Layout.module.css';
export default function Layout({ children }) {
  const data = useStaticQuery(graphql`
    {
      file(relativePath: { eq: "coffee.jpg" } ) {
        childImageSharp {
          fluid {
            ...GatsbyImageSharpFluid
          }
        }
      }
    }
  `);
  return (
    <div>
      <BackgroundImage
        id={styles.header}
        fluid={data.file.childImageSharp.fluid}>
        <div id={styles.inner}>
          <h1><Link to="/">Joe's Coffee Shop</Link></h1>
          <Link to="/blog">Blog</Link>
          <Link to="/menu">Menu</Link>
        </div>
      </BackgroundImage>
      <main id={styles.main}>
        {children}
      </main>
    </div>
  );
}
Listing 11-7

Updating the Layout component to use a static query

In the GraphQL query, we are using the file field to select a specific file – the header background image. The fluid image data is passed to the BackgroundImage component as before and replaces the header element we had previously.

There’s one more change we need to make. We need to remove the hard-coded background image from the Layout component’s CSS module. Open the file src/components/Layout.module.css and remove the background property in the selector for the #header element . The CSS module should now look like the code in Listing 11-8.
#header {
  font-family: 'Oswald', sans-serif;
  background-size: cover;
  color: #FFFFFF;
}
#header #inner {
  background: rgba(119, 79, 56, 0.85);
  padding: 1rem;
  display: flex;
  align-items: center;
}
#header h1 {
  margin: 0;
  flex-grow: 1;
}
#header h1 a {
  color: #FFFFFF;
  text-decoration: none;
}
#header a {
  color: #FFFFFF;
  text-decoration: none;
  margin: 0.5rem;
}
Listing 11-8

The updated CSS module for the Layout component

Finally, restart the development server and go to the index page. The coffee beans image should appear in the header as before, but if you check the developer tools, the size of the image is much smaller this time – likely well under 1 MB.

We’ve made several changes to the project. Let’s remember to commit and push so that the live version of our site is updated on Netlify.
git add .
git commit -m "Add dynamic image loading"
git push origin master

The gatsby-image package

We have used the gatsby-background-image package, which gives us the BackgroundImage component for setting a background image on an element. But what about regular images defined with an img tag?

For that, we can use the gatsby-image package. Similar to gatsby-background-image , it provides an Img component that has fixed and fluid properties to accept data from the corresponding GraphQL fragment. This is meant to replace an img tag with the dynamic image data.

It provides the same benefits as the BackgroundImage component, including properly sized images and the “blur-up” effect when loading the image.

Summary

In this chapter, we
  • Installed various Gatsby image processing plugins

  • Used the BackgroundImage component from the gatsby-background-image package to use dynamic image data to improve image loading and page load time on the index page’s hero image and the layout’s header image

  • Briefly discussed the gatsby-image package

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.14.79.63