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:

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: [
      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) {
      edges {
        node {
          frontmatter {
            date(formatString: "DD MMMM, YYYY")
            cover_image {
              childImageSharp {
                sizes(maxWidth: 1240 ) {
          fields {

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 and nodes, 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:

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,

Keep Reading:


Oscar is an artist and engineer who's been creating cannabis brands and media experiences for over 10 years, and developing full-stack applications for over 15 years.