With the new year comes 100 Days of Gatsby, a challenge for both new and experienced Gatsby developers to get more familiar with the framework. I had intended to drill a lot deeper into Gatsby this year, so this dovetails nicely with that plan. The first major victory I had from working on the challenge is a much deeper level of comfort with working with Gatsby's data layer and how that feeds into the major Gatsby APIs, like Gatsby Image. For those newer to GraphQL or graph theory, the jargon and concepts can be a bit of a heavy lift, but they can definitely be demystified with a careful approach. Let's look at how I added the Image API to my in-progress portfolio to see how it works!
The What and Why of Gatsby Image
Gatsby Image is a library feature that takes advantage of the Sharp image processing library to perform certain image optimizations automatically. It offers these optimizaations on both fixed or fluid images, lets you decide the level of optimization, and gived fine-grained control over effects and features like blur-up or lazy-loading. While it's not a total replacement for the <img />
element in all use cases, it does often save developers a lot of work.
Installing Dependencies
To begin, we need to be sure that three specific plugins are installed:
-
gatsby-image
, which does the bulk of the work in the Image API. -
gatsby-transformer-sharp
, which creates the specific node (more on nodes later in the article if this is an unfamiliar concept) that the library needs to work it's magic. -
gatsby-plugin-sharp
, which performs several other functionsfor the Sharp library.
Depending on whether or not you used a starter and what specific starter you used (like the default starter, you may already have these dependencies installed and listed in your gatsby-config.js
file. If not, install them via npm or yarn per the instructions on the offical docs, and add them to your gatsby-config.js
. You also need a plugin to assist in querying for that data, which is often going to be covered by gatsby-source-filesystem
. Let's have a look at the relevant portion of my gatsby-config
:
plugins: [
`gatsby-plugin-react-helmet`,
{
resolve: `gatsby-source-filesystem`,
options: {
name: `images`,
path: `${__dirname}/src/images`,
},
},
`gatsby-plugin-emotion`,
`gatsby-transformer-sharp`,
`gatsby-plugin-sharp`,
Certain plugins in Gatsby call for more configuration that simply listing the plugin name, and gatsby-source-filesystem
is one of those. I need to make an important distinction here about queries and paths that isn't necessarily spelled out in the Gatsby Image docs, and is approached from several angles in different places in the docs, so it might trip up newcomers to this API: When giving an argument to the query in GraphiQL (which we'll cover in a minute), the relative path begins from the value fed to the 'path' key in the 'options' object. So if you have an images
folder specified here like I have, you don't need to include that in the query. Now that this setup is complete, let's move on to the data layer of Gatsby!
Querying For Data
One of the nicer features that using GraphQL in Gatsby offers is GraphiQL, a built-in interface for writing and testing operations involving the data in your application. When you run your site using gatsby develop
, npm start
, or whatever the start command you've assigned is, you'll notice two URLs logged to the console: one where the site is loaded (typically localhost:8000), and one that takes you to the GraphiQL tool (usually localhost:8000/__graphiql). Let's have a look at GraphiQL:
The column on the left is the Explorer, which gives you a guided overview of the possible arguments that you can use to construct queries. Checking a checkbox automatically makes that field appear in the editor area in the middle of the screen, which saves a lot of time typing, especially when you have long arguments.
The middle area is the editor where you enter queries. We'll talk about the content contained here momentarily.
The right-hand side column is the output area, where we see the results of queries. If you notice the tab in the top right corner, you'll notice the tab marked "Docs". That serves as a searchable glossary that contains information about entities that includes what their types are, what arguments they take, etc.
GraphQL's base syntax consists of queries written in a form that resembles a JSON object, but with the keys on the left-hand side only. Let's break down this query:
Queries begin with the
query {}
object enclosing everything.Next, I'm looking for a file that searches the relative path (which was stated earlier in the
gatsby-config.js
file in thegatsby-source-filesystem
) for a file named 'laptop.jpg':
file(relativePath: { eq: "laptop.jpg" }) {}
Now, I pass in the
childImageSharp {}
object to gain access to the Sharp functionality.Then, I specify that it's
fluid(maxWidth: 800) {}
. The maxWidth argument here somewhat confusingly is intended to be the max width of the image's parent element, NOT the image itself. Based on this value, it will generate several different widths keyed off of the detected window width.Finally, I pass in the fields of
src
(where the file comes from), andsrcSet
(variations of the file, based on parent width). If you read Gatsby Image docs, you'll often see a destructured argument like...GatsbyImageSharpFixed
passed in as this final argument. There is a bug with this statement where if some people use it in GraphiQL, it returns a false negative for queries, so be aware of this and use individual arguments in GraphiQL. In my experience, using the destructed object always throws an error in GraphiQL, but works just fine when using in Gatsby code.
The ability to test queries in GraphiQL is a killer feature, ensuring us that we're getting what we expect without the need to set up tests for each and every query (not that this tyoe of query verification replaces tests, mind you), so that we don't introduce them into the code itself until we're sure it's ready.
Adding an Image Query to the Front End
To use this query in my site, I first have to import some things from core 'gatsby', which I'll explain as I utilize:
import { useStaticQuery, graphql } from "gatsby"
..and also import Img from "gatsby-image"
, which we need to replace the standard <img />
tag.
useStaticQuery
is the Hook version of the Gatsby static query that makes queries within the code. While useStaticQuery
is slightly cleaner looking than a standard static query, per the Gatsby docs....
Because of how queries currently work in Gatsby, we support only a single instance of useStaticQuery in a file
...so don't use useStaticQuery
if you'll need to make multiple queries in a file. Now, let's look at the query variable itself:
const IndexPage = () => {
const data = useStaticQuery(graphql`
query {
file(relativePath: { eq: "laptop.jpg" }) {
childImageSharp {
fixed(width: 300, height: 200) {
...GatsbyImageSharpFixed
}
}
}
}
`)
return (
<Layout>
From the outside in, I am:
Passing the query (slightly modified from the initial example) to the
graphql
tagged template literal, which is why we needed to import that particular dependency.I then pass that statement as the sole argument to
useStaticQuery
.Finally, I assign the entire statement to the
data
variable.
Note that this variable is declared immediately inside the component declaration, but before the 'return` statement, as is often in React.
The finished element looks like this:
<Img fixed={data.file.childImageSharp.fixed} alt="Picture of a laptop" />
If was a fluid image, I would be passing the data object to a prop marked 'fluid', and I would replace the 'fixed' part of the argument with fluid, but it would otherwise be the same.
And it's showing up on the page!
In Closing
Querying for and configuring images in Gatsby can be intimidating at first and is honestly one of the more laborious APIs to work with in this framework, but is a skill every Gatsby dev will want to be adept in. I hope this piece helped you understand Gatsby Image more completely!
Top comments (0)