Code
Building a reusable gatsby-image component

21 April, 2019

TLDR: You can't. Yet. However there's a discussion about how to do this using Gatsby's <StaticQuery>. See Issue#10482.

Luckily there's a (hopefully temporary) solution.

The Problem

Assuming you've already got gatsby-image in place and working, you can query for an optimized image like so.

import React from 'react';
import { graphql, useStaticQuery } from 'gatsby';
import Img from 'gatsby-image';
export default function StaticImage() {
const data = useStaticQuery(graphql`
query {
file(relativePath: { eq: "avatars/austin-paquette.jpeg" }) {
childImageSharp {
# Specify the image processing specifications right in the query.
# Makes it trivial to update as your page's design changes.
fixed(width: 125, height: 125) {
...GatsbyImageSharpFixed
}
}
}
}
`);
return <Img fixed={data.file.childImageSharp.fixed} />;
}
view raw StaticImage.jsx hosted with ❤ by GitHub

Note that I'm using the useStaticQuery hook in my example. Your code will look different if you use the <StaticQuery> component.

The problem arises when you want to pass dynamically pass in a value that modifies the avatars/austin-paquette.jpeg string.

import React from 'react';
import { graphql, useStaticQuery } from 'gatsby';
import Img from 'gatsby-image';
export default function StaticImage() {
const data = useStaticQuery(graphql`
query {
file(relativePath: { eq: "avatars/austin-paquette.jpeg" }) {
childImageSharp {
# Specify the image processing specifications right in the query.
# Makes it trivial to update as your page's design changes.
fixed(width: 125, height: 125) {
...GatsbyImageSharpFixed
}
}
}
}
`);
return <Img fixed={data.file.childImageSharp.fixed} />;
}
view raw StaticImage.jsx hosted with ❤ by GitHub

Normally we'd pass in a variable, but since we cannot do that. Our hands are a bit tied.


Solution Concept

Until variables are available to be passed through, our solution is:

  • Query for all of the images
  • Find the image in particular that we need
  • Return the <Img> component with the target image data

This quickly falls apart at scale however, querying for all images on repeat just simply won't do. A small revision to something maintainable and we end up with:

  • Query for a category (or cluster) of images
  • Find the image in particular that we need
  • Return the <Img> component with the target image data

The key here is separating the images out. How that's done is entirely up to you. This is inspired by a discussion on Gatsby's Spectrum Chat


Solution Code

So, let's see how this works in practice. First, we need to query for all images.

const images = useStaticQuery(graphql`
{
allFile(
filter: {
dir: { regex: "/src/images/" }
extension: { regex: "/jpeg|jpg|png|gif/" }
}
) {
edges {
node {
extension
relativePath
childImageSharp {
fluid(maxWidth: 1920) {
...GatsbyImageSharpFluid
}
}
}
}
}
}
`);

At this point we could have a huge set of returned data. If you want to take advantage of diving deeper with your images, you can extend the dir filter.

dir: { regex: "/src/images/" }

could be extended to

dir: { regex: "/src/images/blog/" }

Then we will find only the targetted result that we need, run that through a render function, that returns the gatsby-image component we want.

function renderImage(file) {
return (
<Img fluid={file.node.childImageSharp.fluid} />
);
}
export default function DynamicImage({ image }) {
// ...
return renderImage(
images.allFile.edges.find((file) => file.node.relativePath === image)
);
}
view raw RenderFunction.jsx hosted with ❤ by GitHub


Full Code & Usage

import * as React from 'react';
import { graphql, useStaticQuery } from 'gatsby';
import Img from 'gatsby-image';
function renderImage(file) {
return (
<Img fluid={file.node.childImageSharp.fluid} />
);
}
export default function DynamicImage({ image }) {
const images = useStaticQuery(graphql`
{
allFile(
filter: {
dir: { regex: "/src/images/" }
extension: { regex: "/jpeg|jpg|png|gif/" }
}
) {
edges {
node {
extension
relativePath
childImageSharp {
fluid(maxWidth: 1920) {
...GatsbyImageSharpFluid
}
}
}
}
}
}
`);
return renderImage(
images.allFile.edges.find((file) => file.node.relativePath === image)
);
}
view raw DynamicImage.jsx hosted with ❤ by GitHub

Usage

import React from 'react';
import DynamicImage from '../path/to/your/components';
export default function Usage() {
return <DynamicImage image="image_name.jpg" />;
}
view raw Usage.jsx hosted with ❤ by GitHub