November 1, 2018
I used to 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 - they 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 👋
I'm Noah, a software developer based in the San Francisco Bay Area. I focus mainly on full stack web and iOS development