Easy Gatsby Image Components

November 1, 2018

I use Gatsby as a static site generator to build this blog using React. As part of this, I wanted a simple component to display a static image, something like this:

<Image filename="myimage.png" />

Surprisingly, this is hard to do in Gatsby. The default Image component generated by Gatsby uses StaticQuery:

import Img from 'gatsby-image'

const Image = () => (
  <StaticQuery
    query={graphql`
      query {
        placeholderImage: file(relativePath: { eq: "gatsby-astronaut.png" }) {
          childImageSharp {
            fluid(maxWidth: 300) {
              ...GatsbyImageSharpFluid
            }
          }
        }
      }
    `}
    render={data => <Img fluid={data.placeholderImage.childImageSharp.fluid} />}
  />
)

Simple enough - I figured I could make it customizable using props:

const Image = (props) => (
  <StaticQuery
    query={graphql`
      query {
        placeholderImage: file(relativePath: { eq: ${props.filename} }) {
          childImageSharp {
            fluid(maxWidth: 300) {
              ...GatsbyImageSharpFluid
            }
          }
        }
      }
    `}
    render={data => <Img fluid={data.placeholderImage.childImageSharp.fluid} />}
  />
)

Unfortunately this doesn't actually work - StaticQuery is called "Static" because it's compiled and doesn't support string interpolation in its template literal. If you try to do this, you'll see an error like this on build:

Error: BabelPluginRemoveGraphQL: String interpolations are not allowed in graphql fragments. Included fragments should be referenced as `...MyModule_foo`.

This behavior is documented by Gatsby, and that documentation notes that the alternative is to use a page query. I didn't want to put a query on every page with images though, so I started investigating other options.

I came across this post by someone who had been doing some fancier image querying with Gatsby - she had the foresight to query all the images upfront by using the allFile query, and filter them down with props later. Based on that, I implemented the following component, which works great and includes all the fun functionality that gatsby-image offers:

import Img from 'gatsby-image'

const Image = (props) => (
  <StaticQuery
    query={graphql`
      query {
        images: allFile {
          edges {
            node {
              relativePath
              name
              childImageSharp {
                sizes(maxWidth: 600) {
                  ...GatsbyImageSharpSizes
                }
              }
            }
          }
        }
      }
    `}

    render={(data) => {
      const image = data.images.edges.find(n => {
        return n.node.relativePath.includes(props.filename);
      });
      if (!image) { return null; }
      
      const imageSizes = image.node.childImageSharp.sizes;
      return (
        <Img
          alt={props.alt}
          sizes={imageSizes}
        />
      );
    }}
  />
)

Hopefully that snippet helps if you're trying to do something similar. If it does, let me know 👋