3

I've been following this tutorial https://www.gatsbyjs.org/packages/gatsby-image/#art-directing-multiple-images but there is no way normal people would write 50 lines of code for adding an image:

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

export default ({ data }) => {
  // Set up the array of image data and `media` keys.
  // You can have as many entries as you'd like.
  const sources = [
    data.mobileImage.childImageSharp.fluid,
    {
      ...data.desktopImage.childImageSharp.fluid,
      media: `(min-width: 768px)`,
    },
  ]

  return (
    <div>
      <h1>Hello art-directed gatsby-image</h1>
      <Img fluid={sources} />
    </div>
  )
}

export const query = graphql`
  query {
    mobileImage: file(relativePath: { eq: "blog/avatars/kyle-mathews.jpeg" }) {
      childImageSharp {
        fluid(maxWidth: 1000, quality: 100) {
          ...GatsbyImageSharpFluid
        }
      }
    }
    desktopImage: file(
      relativePath: { eq: "blog/avatars/kyle-mathews-desktop.jpeg" }
    ) {
      childImageSharp {
        fluid(maxWidth: 2000, quality: 100) {
          ...GatsbyImageSharpFluid
        }
      }
    }
  }
`

My question is, how can you keep your sanity and use images with gatsby?

This example has many issues:

  • Import and usage are far away, idealy should be close (therefore if you remove from html, you need to remember to delete from graphql)
  • Amount of boilerplate is huge. Imagine you need 2 images...
  • No auto complete when importing images. Devs really type the full path of the images? That seems a lot of work. Renaming looks risky too. (I'm using intellij.)

For anyone else using typescript and wants to improve perf a bit by filtering only for images :

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

interface Props {
  // @todo: convert to enum
  relativePath: string;
  alt: string;
}

export const Image: React.FC<Props> = (props) => {
  const {relativePath, alt} = props

  const images: { data: { edges: Array<{ node: { relativePath: string, default: { fluid: any } } }> } } = useStaticQuery(graphql`
    query ImageQuery {
      data: allFile(filter: {sourceInstanceName: {eq:"images"}}) {
        edges {
          node {
            relativePath
            default: childImageSharp {
              fluid {
                ...GatsbyImageSharpFluid
              }
            }
          }
        }
      }
    }
  `)

  const image = images.data.edges.find(n => {
    return n.node.relativePath.includes(relativePath)
  })

  if (!image) {
    throw new Error(`Image not found`)
  }

  return (
    <Img alt={alt} fluid={image.node.default.fluid} />
  )
}

To add sourceInstanceName

/**
 * From:
 *  - https://github.com/ChristopherBiscardi/gatsby-mdx/issues/105
 *  - https://github.com/gatsbyjs/gatsby/issues/1634
 */
export const onCreateNode = ({ node, getNode, actions }) => {
  const { createNodeField } = actions
  if (node.internal.type === `MarkdownRemark`) {
    const parent = getNode(node.parent)
    if (parent.internal.type === 'File') {
      createNodeField({
        name: `sourceInstanceName`,
        node,
        value: parent.sourceInstanceName,
      })
    }
  }
}

and plugin config

    {
      resolve: `gatsby-source-filesystem`,
      options: {
        path: props.imageRootFolder,
        name: 'images',
      },
    },
07mm8
  • 2,702
  • 2
  • 8
  • 20

1 Answers1

2

I use a generic Image component:

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

export default (props) => {

  const { filename, type = 'default', alt, sizes = '(max-width: 400px) 100px, (max-width: 800px) 200px, 400px' } = props;

  const images = useStaticQuery(graphql`
    query ImageQuery {
      data: allFile {
        edges {
          node {
            relativePath
            default: childImageSharp {
              fluid {
                ...GatsbyImageSharpFluid
              }
            }
            square: childImageSharp {
              fluid(maxWidth: 600, maxHeight: 600) {
                ...GatsbyImageSharpFluid
              }
            }
          }
        }
      }
    }
  `);

  const image = images.data.edges.find(n => {
    return n.node.relativePath.includes(filename);
  });

  if (!image) {
    return null;
  }

  return (
    <Img alt={alt} fluid={{
      ...image.node[type].fluid,
      sizes: sizes,
    }} />
  )
}

Then I pass the filename and alt text (and optionally type and sizes).

 <Image alt="Gravity does not apply to cats" type="square" filename="cat-defies-gravity.png" />

I agree this is a workaround until we get something like Querying 2.0. If you read that page you will see exactly your problem as an example.

Albert Skibinski
  • 499
  • 4
  • 21
  • looks decent, but if you ever need a variation of that query, you would need to create a new component, right? I need a mix of fixed and fluid images. This seems like a huge trade off... anyway, let's hope will be fixed in q 2.0 – 07mm8 May 02 '20 at 07:22
  • Actually, you could put that in one query with aliases. Something like: ``` square: childImageSharp { fluid(maxWidth: 600, maxHeight: 600) { ...GatsbyImageSharpFluid } } fluid: childImageSharp { fluid { ...GatsbyImageSharpFluid } } fixed: childImageSharp { fixed { ...GatsbyImageSharpFluid } } ``` – Albert Skibinski May 02 '20 at 09:24
  • I editted my original answer and added a "type" which you can use for different variations of the query. You could expand that to include fluid/fixed variations. And it propable should get refactored a bit into an options object now, but you get the idea. – Albert Skibinski May 02 '20 at 09:45
  • I see what you mean. Now we will create a ton of images for each type, correct? – 07mm8 May 02 '20 at 10:15
  • but at runtime this might be very slow as it needs to loop all images to render: https://github.com/gatsbyjs/gatsby/issues/11913 – 07mm8 May 05 '20 at 05:15