Working with images is boring. If you were to include an image on a website, you have to assure:
- That the images are properly resized for each screen
- That we serve the right image based on the device pixel density
- That we serve modern image formats when possible
- That the images are compressed
But also..
- That we don't load all the page at once, consuming bandwidth, when the visitor might not even scroll to see most of the pages
- That we don't cause layout movements, as the images abruptly load
Thankfully, Gatsby offers some nice utilities, bundling all the steps required into a single tool-chain, letting us focus on other things.
To use GraphQL or not?
Up to recently, if you didn't want to go the GraphQL route, you were all out of luck. Thankfully, now it's possible with the help of the StaticImage
component. But let's take a step back for a moment.
Gatsby is a fantastic framework if you want to consume data from various remote sources. Fetch the data (instagram, wordpress, etc), push them into the GraphQL layer, and populate your pages based on that data. To help with the images that these sources include, the Gatsby team & collaborators added a set of utilities to optimize them. But for everything outside the GraphQL layer, there wasn't anything.
That resulted in an unnecessary boilerplate. If you had a simple local image but wanted a cool blur effect, you had to go through GraphQL. That said, with the latest gatsby-plugin-image
we have the solution, and we can safely go one way or the other depending on the use case.
- If the image comes from a remote source, you're already using GraphQL
- If the image is part of a collection, like the cover of a blog post, you will have an easier time with GraphQL
- If the image is the 404 illustration, the image of a section, or something ephemeral, inlining is the best way to go.
Inlining images
If your images don't go through the Gatsby GraphQL layer, you can use StaticImage
The same configuration object that we declare in the GraphQL schema, can be passed as props. Rejoice.
GraphQL Configuration
Dependencies
First of all, we need to ensure we have all the dependencies in place.
We need 4 plugins before we start:
- gatsby-source-filesystem, to make the images known the GraphQL data layer. You probably already have this installed.
- gatsby-plugin-image, which exposes the
StaticImage
&GatsbyImage
components - gatsby-plugin-sharp, to bridge the gap between Sharp and the rest of the plugins
- gatsby-transformer-sharp, to manipulate the images using GraphQL queries
Types of images
Now, this is where it gets interesting - we can have three types of responsive images.
- Images with fixed width. When knowing exactly how big the images should be. (
FIXED
) - Images that stretch across their fluid parent container. Completely dependent on their parent, who can take many shapes and forms between screen sizes. (
FULL_WIDTH
) - Images that stretch across their container but limited to a maximum width (
CONSTRAINED
)
Ok, this might be confusing. The difference between FULL_WIDTH
& CONSTRAINED
can be seen in the following table.
The FULL_WIDTH
image will expand to fill its container, even if it looks blurred.
So assuming we want a CONSTRAINED
image and we don't want image copies bigger than 200px, here's our query.
This setting will make sure to include copies for both jpg
and webp
, even if we don't specifically request for the latter.
Placeholders
Everything is in order, but we probably want a smooth fallback. Here are our options:
BLURRED
: (default) a blurred, low-resolution image, encoded as a base64 data URITRACED_SVG
: a low-resolution traced SVG of the imageDOMINANT_COLOR
: a solid color, calculated from the dominant color of the image.NONE
: no placeholder. Looks better with thebackground
prop set.
Here are the first three options side to side. I tend to prefer the first two.
Blurred | Dominant | Traced |
---|---|---|
Transforms
Gatsby allows us to do some transforms too:
- grayscale
- duotone
- rotate
- trim
- cropFocus
- fit
Frankly, Gatsby is a bit notorious for its build times, so I would advise to do a pre-processing to avoid transforming the same images again and again. If that's not possible, this option is for you.
Consuming the images
Contrary to the StaticImage
example, GatsbyImage
accepts an image
prop.
Now writing album.cover.childImageSharp.gatsbyImageData
is a bit tedious, so we can import getImage
from the very same package, and refactor it as follows:
Referencing images
You probably want to fetch the images dynamically, as part of another entity. The best way for that is to link the images with the rest of the metadata. Here's an example from my blog.
And the call with the rest of the metadata
Images in markdown files
Unfortunately, gatsby-plugin-image
doesn't help with markdown files. We can optimize any images we query along with our data, but the images which are referenced inside the markdown file, won't be touched.
In order to do this, we have to include a separate plugin gatsby-remark-images. Now to keep things tidy, I like to keep my blog images near the markdown file, and copy them over with gatsby-remark-copy-linked-files.
Here's how it looks
And here how it's written
Now having installed the plugins, we add some basic options and we're ready to go.
Not as polished as the gatsby-plugin-image
, but it does the trick.
Fin
At the time of writing gatsby-plugin-image
is still in beta. That said, I'm using it because life is too short to not live on the edge.
If you want to follow the official documentation, you can find it here
👋