1

So I have the image component that renders 2 different images, but I want to be able to just add my image component to any page and just pass in the specific image as a prop instead of having to hard code a new query for each image I need to use.

If I had 50 images, I'd like to just pass image-50.jpg in as a prop instead of making a specific query for it. Is there a way to do that with graphql in gatsby?

Here is my current image component code

      import { graphql, useStaticQuery } from "gatsby"
      import Img from "gatsby-image"
      import React from "react"

      const Image = () => {
        const data = useStaticQuery(graphql`
          query {
            astronaut: file(relativePath: { eq: "gatsby-astronaut.png" }) {
              childImageSharp {
                fluid(maxHeight: 600) {
                  ...GatsbyImageSharpFluid
                }
              }
            }
            person: file(relativePath: { eq: "profile.jpg" }) {
              childImageSharp {
                fluid(maxHeight: 600) {
                  ...GatsbyImageSharpFluid
                }
              }
            }
          }
        `)

        return (
          <>
            <Img fluid={data.astronaut.childImageSharp.fluid} />
            <Img fluid={data.person.childImageSharp.fluid} />
          </>
        )
      }

      export default Image

Is there a way to just add the image dynamically and then render it to a new page?

Something like this <Image src={"profile.jpg"} />

I don't know how I could add that to the graphql and imagine I have 50 images, then I would have to either map through all 50 or manually add each query and that doesn't make sense

designxd10
  • 57
  • 8
  • Does this answer your question? [Reusable Gatsby-Image Component with dynamic image sources](https://stackoverflow.com/questions/55122752/reusable-gatsby-image-component-with-dynamic-image-sources) – Ferran Buireu Sep 16 '20 at 04:51

2 Answers2

2

Believe it or not you cannot create a fully dynamic image component using a gastby-image without risking a (possibly very large) bloat in your bundle size. The problem is that static queries in Gatsby do not support string interpolation in it's template literal. You would need to search through all the files each time you use the component. There are some solutions you can try in an existing SO post found here.

You can always use graphql fragments and write something like the below for your queries and then conditionally render the proper image based on a file name passed via props in your Image component but alas this also pretty clunky:

export const fluidImage = graphql`
fragment fluidImage on File {
  childImageSharp {
    fluid(maxWidth: 1000) {
      ...GatsbyImageSharpFluid
    }
  }
}
`;

export const data = graphql`
  query {
    imageOne: file(relativePath: { eq: "one.jpg" }) {
      ...fluidImage
    }
    imageTwo: file(relativePath: { eq: "two.jpg" }) {
      ...fluidImage
    }
    imageThree: file(relativePath: { eq: "three.jpg" }) {
      ...fluidImage
    }
  }
`
// accessed like this
<Img fluid={data.imageOne.childImageSharp.fluid} />
// or this
<Img fluid={data.imageTwo.childImageSharp.fluid} />
// or this, dynamically (if you had a prop called imageName)
<Img fluid={data.[`${props.imageName}`].childImageSharp.fluid} />
apena
  • 2,091
  • 12
  • 19
1

As Apena's answer explains, it's tricky to work like that with Gatsby's Image. However, I must say that you can bypass it in different ways depending on the filesystem used and how the data is structured.

Keep in mind that if you set properly the filesystem in your gatsby-config.js, you are allowing Gatsby to recognize and to find all your images in your project, making them queryable and allowing them to be used by Gatsby Image component.

const path = require(`path`)

module.exports = {
  plugins: [
    {
      resolve: `gatsby-source-filesystem`,
      options: {
        name: `images`,
        path: path.join(__dirname, `src`, `images`),
      },
    },
    `gatsby-plugin-sharp`,
    `gatsby-transformer-sharp`,
  ],
}

You can find much better ways than querying each image in a staticQuery filtered by the path, it's not true that is the only way to achieve it. Of course, if you are using a staticQuery approach, the limitation of making it dynamic forces you to make each query separately.

First of all, you need to know the difference between staticQuery and page query to understand which fits you and the limitations of them.

If you use a page query, you can always create an approach like the following one:

import React from 'react'
import { graphql } from 'gatsby'
import Layout from '../components/layout'

class ProductPage extends React.Component {
  render() {

    const products = get(this, 'props.data.allDataJson.edges')

    return (
      <Layout>
        {products.map(({ node }) => {
          return (
            <div key={node.name}>
              <p>{node.name}</p>
              <Img fluid={node.image.childImageSharp.fluid} />
            </div>
          )
        })}
      </Layout>
    )
  }
}

export default ProductPage


export const productsQuery = graphql`
  query {
    allDataJson {
      edges {
        node {
          slug
          name
          image{
            publicURL
            childImageSharp{
              fluid {
                ...GatsbyImageSharpFluid
              }
            }
          }
        }
      }
    }
  }
`

In the example above, you are using a page query to retrieve all images from a JSON file. If you set the path in your filesystem, you will be able to retrieve them using the GraphQL fragments. This approach is the more dynamic you can afford when dealing with Gatsby Image and it's better to query one by one.

The idea remains the same for other filesystems, this is just an adaptable approach. If you are using a CMS like Contentful, you can download the assets and query them dynamically in the same way since the filesystem allows you to do it.

Pages queries are only allowed in page components (hence the name) so, if you want to use it in a React standalone component to make it reusable, you will need to pass via props (or reducer) to your desired component and render the Gatsby image based on the received props.

Ferran Buireu
  • 28,630
  • 6
  • 39
  • 67