Adding Images and Media to your GatsbyJS Blog
In our last tutorial we built a static React-powered blog using the GatsbyJS framework and static site generator. One of the major features we didn't cover was the handling of images and media we want to include in our Markdown content. In this tutorial, we'll cover how to add a cover image to your blog articles.
What's the big deal with images?
The way GatsbyJS works is by compiling our blog's assets into static content. The assets include things like the React components, CSS, and any images we directly include in the components or CSS. So when you run your build, your page component /about.js
will become an HTML file in the static folder.
But when it comes to images queried by the GraphQL, Gatsby doesn't automatically copy these assets into our static build. If you try and link to a local file, you'll find it doesn't exist on your development or production server.
There's a whole page of the GatsbyJS docs that talks about the implementation of this feature -- but they seem forgotten, exchanged in favor for a few plugins that accomplish the task of copying media assets. The plugins work fantastically, and implement the functionality we need with an approachable API.
Lets get building.
If you haven't already, make sure to complete the first part of this tutorial or download a starter from Github here.
The plugins
We'll need to install 4 plugins:
- gatsby-plugin-sharp - This plugin allows us to use the Sharp image processing library to resize and compress our images.
- gatsby-transformer-sharp - This plugin allows us to query the images on GraphQL through special
ImageSharp
nodes. - gatsby-remark-copy-linked-files - This plugin does the magic of grabbing any files we link in our Markdown, like images or even PDFs, and copies them to the
./public
folder. - gatsby-remark-images - This plugin makes all the images responsive, giving us access to a
<Img />
component that automatically scales depending on the user's device size.
Let's install them all:
npm i --save gatsby-remark-images gatsby-plugin-sharp gatsby-remark-copy-linked-files gatsby-transformer-sharp
Then add this to your gatsby-config.js
file to let Gatsby know we have new plugins (and how to use some of them):
{
plugins: [
`gatsby-remark-copy-linked-files`,
`gatsby-transformer-sharp`,
`gatsby-plugin-sharp`,
{
resolve: `gatsby-remark-images`,
options: {
maxWidth: 1080,
},
},
],
}
You can set the maxWidth
of your images in the options to the size of your biggest image container.
Add some images to Markdown
We'll be adding a cover_image
field to our Markdown files to show a big header image above posts, and to use as a thumbnail on archives. We'll add it to the frontmatter of the Markdown file to make it easy.
Open up one of the Markdown files in the src/content/blog/
folder and add a cover_image
field to the frontmatter:
---
title: "Using a Framework to Simplify Email Design"
date: "2017-08-10"
section: blog
cover_image: "./foundation-emails-guide@1x.jpg"
tags: design, development
---
Querying the images
Let's open up the first page of the site and query up some images there. Head over to src/pages/index.js
and scroll to the bottom where the GraphQL query is.
We're going to use the new childImageSharp
node in GraphQL to grab responsive versions of the images. You can either scale by a certain size, or by resolution ratio. We'll be scaling by size.
Replace the entire query
variable with this one:
export const query = graphql`
query IndexQuery {
allMarkdownRemark(sort: {fields: [frontmatter___date], order: DESC}, limit: 3) {
totalCount
edges {
node {
id
frontmatter {
title
date(formatString: "DD MMMM, YYYY")
cover_image {
publicURL
childImageSharp {
sizes(maxWidth: 1240 ) {
srcSet
}
}
}
section
}
fields {
slug
}
excerpt
}
}
}
}
`;
Let's break the query down a bit. We call allMarkdownRemark
, which accesses all Markdown files on our server. We also sort
by the date and order it DESC. Then inside, we ask for fields we want to show on the page. In our case, we want to see things like the title and date, nested inside the frontmatter field.
If the syntax of GraphQL throws you for a loop, and you don't understand why you have
edges
andnodes
, don't get stressed. I was a little confused at the start too since my background is in SQL.
I'd recommend taking a look at the GraphQLi query builder, usually at http://localhost:8000/___graphql, or just add ___graphql
to the end of your development server. This pulls up a live query builder during development that helps ensure you build a properly formatted query. If you click inside, start a new line, and press any letter on your keyboard -- you'll see a tooltip popup with all the possible options for fields.
This helps immensely when making new queries from scratch, or just trying to seeing what fields are available at certain depths.
The Responsive Component
Now that we've queried the images, let's actually see them!
Here we'll be using the <Img />
component (not to be confused with <img src...>
) from the gatsby-image
package. You can read the plugin page for details, but it basically creates a responsive image nested inside a couple divs. If the user loads mobile, it loads a smaller image, and stretches it accordingly to fit the size. It also does stuff like the blurred placeholder loading image (ala Facebook or Medium).
To use the <Img />
component, import the gatsby-image
package:
import Img from "gatsby-image";
And then add the <Img />
tag anywhere in your code:
<Img sizes={featured.frontmatter.cover_image.childImageSharp.sizes} />
We reach into the frontmatter to grab the cover_image, which now has a ImageSharp
node (because we added the plugin above). Though we use childImageSharp
, probably because it's not a direct image node, but a image attached to a field. The ImageSharp
node provides us with a sizes
array containing various images at different viewport sizes. This all gets fed into the <Img />
component, which does the heavy lifting of swapping between the set of images depending on the user's device.
That's it!
Once you figure it out and find the right plugins, it's a fairly simple process. And being able to generate queries using the help of the GraphiQL browser, combined with dumping data into the console, makes deciphering the data structure fairly simple.
You can see this code more fleshed out with CSS and whatnot in my personal blog's repo: https://github.com/whoisryosuke/ryosuke-gatsby-blog/
Next time: Paginated Archives
In the next part of this tutorial series we'll be adding a paginated archive for our blog. Each blog post has it's own page, but we need an archive for users -- and SEO spiders, to browse and find our older posts. Keep an eye on the #GatsbyJS tag to see our next post!
If you have any questions or issues: feel free to comment down below, send us a tweet, or contact us through our site's form.
Stay regular,
Oscar
Keep Reading: