An Introduction To Using Gatsby Image

With the rise of mobile browsing, the optimization of images on a website is more important than ever. If a site is image heavy, it's always a good idea to lazy load images that are further down the page to speed up the initial render. However, optimizing and implementing lazy loading images can be pretty time consuming. Gatsby-image is a React component that works with Gatsby.js to give you an easy way to load and optimize images on a website.

If you haven't already, go through the official Gatsby.js tutorial first, you'll need to know some of the basics of Gatsby and GraphQL before we get started. Here is a demo of what we'll be building:

Gatsby Image Demo ( view source )

Let's start off by installing and running the gatsby-starter-blog:

gatsby new gatsby-image-demo https://github.com/gatsbyjs/gatsby-starter-blog
cd gatsby-image-demo
gatsby develop

Go to http://localhost:8000/ and you'll see something like this. We can now add some images to this starter blog!

Let's make a new directory for our cool new images. From the root folder of the project:

cd src/
mkdir images

One of my go-to websites for free stock images is https://unsplash.com/. Just download a few images and save them into the new src/images folder. I'm going to save them as one.jpg, two.jpg, and three.jpg for ease of reference.

Going back to the terminal, let's stop the gatsby develop and add the gatsby-image package:

yarn add gatsby-image

Adding your images folder to gatsby-config.js

Now open up gatsby-config.js. Since gatsby-image is a React component, we don't need to add it to the config file. However, we do need to set up our src/images folder using gatsby-source-filesystem so we can use GraphQL to query our images. Look for this object in the config file, in the plugins array:

{
  resolve: `gatsby-source-filesystem`,
  options: {
    path: `${__dirname}/src/pages`,
    name: 'pages',
  },
},

This is telling Gatsby to look in the /src/pages directory for some files, and make "nodes" out of them. These nodes are needed when making the GraphQL queries for the site. This /src/pages directory is where all the current pages and posts are kept. We will add our images directory right under this object and change the path and name options. Your gatsby-config.js should now have this:

{
  resolve: `gatsby-source-filesystem`,
  options: {
    path: `${__dirname}/src/pages`,
    name: 'pages',
  },
},
{
  resolve: `gatsby-source-filesystem`,
  options: {
    path: `${__dirname}/src/images`,
    name: 'images',
  },
},
...

Back into Gatsby

Let's start up Gatsby again by running gatsby develop. The site should look the same as before, but now we can query our new stock images. Open up /src/pages/index.js which is the component for the homepage. Scroll to the bottom and you'll see the GraphQL query that's currently being used:

export const pageQuery = graphql`
  query IndexQuery {
    site {
      siteMetadata {
        title
      }
    }
    allMarkdownRemark(sort: { fields: [frontmatter___date], order: DESC }) {
      edges {
        node {
          excerpt
          fields {
            slug
          }
          frontmatter {
            date(formatString: "DD MMMM, YYYY")
            title
          }
        }
      }
    }
  }
`

This query is currently getting the site's title from the siteMetadata, as well as all the posts from allMarkdownRemark. We'll need to add a query right below allMarkdownRemark for our image one.jpg. The query now looks like this:

export const pageQuery = graphql`
  query IndexQuery {
    site {
      siteMetadata {
        title
      }
    }
    allMarkdownRemark(sort: { fields: [frontmatter___date], order: DESC }) {
      edges {
        node {
          excerpt
          fields {
            slug
          }
          frontmatter {
            date(formatString: "DD MMMM, YYYY")
            title
          }
        }
      }
    }
    imageOne: imageSharp(id: { regex: "/one.jpg/" }) {
      sizes(maxWidth: 630) {
        ...GatsbyImageSharpSizes
      }
    }
  }
`

imageOne will be a new object that's added to this.props.data. Since we are going to put this in the main container of the page, and it will be fluid on mobile, I'm querying for the child object called sizes with a maxWidth of the container size, which is 630px. There's another image type you can query for, called resolutions if you know the exact width and height of the image. Check out the gatsby-image docs for a more detailed explanation on these image types.

Now our one.jpg should be ready to rock and roll. We can take a look at the data for this image by doing a console.log(this.props.data) inside the render() function, and you should see something like this:

https://i.imgur.com/8bRktxJ.png

Importing the gatsby-image component

Going back up to the top of the page, let's import the gatsby-image component:

...
import Img from 'gatsby-image'

class BlogIndex extends React.Component {
...

And add the new component right below the existing <Bio /> component:

...
<Bio />

<Img sizes={this.props.data.imageOne.sizes} />

{posts.map(({ node }) => {
...

Your shiny new image should show up on the site now, with all optimizations in place! We can now add the other two images to our query, and add them to the page. Our full query now looks like:

export const pageQuery = graphql`
  query IndexQuery {
    site {
      siteMetadata {
        title
      }
    }
    allMarkdownRemark(sort: { fields: [frontmatter___date], order: DESC }) {
      edges {
        node {
          excerpt
          fields {
            slug
          }
          frontmatter {
            date(formatString: "DD MMMM, YYYY")
            title
          }
        }
      }
    }
    imageOne: imageSharp(id: { regex: "/one.jpg/" }) {
      sizes(maxWidth: 630) {
        ...GatsbyImageSharpSizes
      }
    }
    imageTwo: imageSharp(id: { regex: "/two.jpg/" }) {
      sizes(maxWidth: 630) {
        ...GatsbyImageSharpSizes
      }
    }
    imageThree: imageSharp(id: { regex: "/three.jpg/" }) {
      sizes(maxWidth: 630) {
        ...GatsbyImageSharpSizes
      }
    }
  }
`

And up in our render() method:

return (
  <div>
    <Helmet title={siteTitle} />
    <Bio />

    <Img sizes={this.props.data.imageOne.sizes} />

    {posts.map(({ node }) => {
      const title = get(node, 'frontmatter.title') || node.fields.slug
      return (
        <div key={node.fields.slug}>
          <h3
            style={{
              marginBottom: rhythm(1 / 4),
            }}
          >
            <Link style={{ boxShadow: 'none' }} to={node.fields.slug}>
              {title}
            </Link>
          </h3>
          <small>{node.frontmatter.date}</small>
          <p dangerouslySetInnerHTML={{ __html: node.excerpt }} />
        </div>
      )
    })}

    <Img sizes={this.props.data.imageTwo.sizes} />
    <Img sizes={this.props.data.imageThree.sizes} />

  </div>
)

If you reload the page, you should see an added "blur up" effect that comes built in with gatsby-image which loads a blurry placeholder image before the real one loads. Scroll down the page to see imageTwo and imageThree lazy load in!

Finished Product ( view source )