Search code examples
graphqlgatsbygatsby-image

In Gatsby how do I pass a relative path file to a template that is also pulling a query?


In Gatsby I've coded a single post template:

singlePost.js:

import React from 'react'
import { graphql } from 'gatsby'
import { MDXRenderer } from 'gatsby-plugin-mdx'
import { H1 } from '../elements'
import { Container, Post, FeatureImage } from '../components'
const singlePost = ({ data }) => {
  const featureImage = data.mdx.frontmatter.featureImg.childImageSharp.fixed
  return (
    <Container>
      <FeatureImage fixed={featureImage} />
      <Post>
        <H1 margin=" 0 0 2rem 0">{data.mdx.frontmatter.title}</H1>
        <MDXRenderer>{data.mdx.body}</MDXRenderer>
      </Post>
    </Container>
  )
}
export const pageQuery = graphql`
  query SinglePostQuery($id: String!) {
    mdx(id: { eq: $id }) {
      body
      frontmatter {
        date
        excerpt
        featureImg {
          childImageSharp {
            fixed(width: 1920) {
              ...GatsbyImageSharpFixed
            }
          }
        }
        title
        slug
      }
    }
  }
`
export default singlePost

In my gatsby-node.js I get the data from the slug:

 data.allMdx.edges.map(edge => {
    const slug = edge.node.frontmatter.slug
    const id = edge.node.id
    actions.createPage({
      path: slug,
      component: require.resolve(`./src/templates/singlePost.js`),
      context: { id },
    })
  })

In the frontmatter of the markdown file there is a feature image:

---
title: Bacon Ipsum
slug: bacon
date: 2021-02-09
featureImg: nature.jpg
excerpt: Bacon ipsum dolor amet pastrami prosciutto meatball fatback, andouille drumstick shank burgdoggen brisket cow turkey.
---

if the post markdown file doesn't have an image I get an error of:

Cannot read property 'childImageSharp' of null

I can access the default image I've set with:

const defaultImage = useStaticQuery(graphql`
  query {
    default: file(relativePath: { eq: "default.jpg" }) {
      publicURL
    }
  }
`)

but when I try to query for the content and the default with:

const defaultImage = useStaticQuery(graphql`
  query SinglePostQuery($id: String!) {
    mdx(id: { eq: $id }) {
      body
      frontmatter {
        date
        excerpt
        featureImg {
          childImageSharp {
            fixed(width: 1920) {
              ...GatsbyImageSharpFixed
            }
          }
        }
        title
        slug
      }
    }
    default: file(relativePath: { eq: "default.jpg" }) {
      publicURL
    }
  }
`)

I get an error of:

If you're not using a page query but a useStaticQuery / StaticQuery you see this error because they currently don't support variables.

In Gatsby how can I query the slug but also have pass the default image to the Feature Image component?


Solution

  • As the error log points, useStaticQuery (because it's a type of static query) doesn't accept variables, hence the name.

    In Gatsby how can I query the slug but also have pass the default image to the Feature Image component?

    You can use the same context where you attach the id to pass the image while using page queries.

      data.allMdx.edges.map(edge => {
            const slug = edge.node.frontmatter.slug
            const id = edge.node.id
            const imagePath = edge.node.featureImg || 'default.jpg'
            actions.createPage({
              path: slug,
              component: require.resolve(`./src/templates/singlePost.js`),
              context: { 
                id 
                imagePath
               },
            })
          })
    

    Note: You may need to query the image in your gatsby-node.js. Change the imagePath to another identifier that matches better your data structure if needed.

    Then, in your template:

    import React from 'react'
    import { graphql } from 'gatsby'
    import { MDXRenderer } from 'gatsby-plugin-mdx'
    import { H1 } from '../elements'
    import { Container, Post, FeatureImage } from '../components'
    const singlePost = ({ data }) => {
      const featureImage = data.mdx.frontmatter.featureImg.childImageSharp.fixed
      return (
        <Container>
          <FeatureImage fixed={featureImage} />
          <Post>
            <H1 margin=" 0 0 2rem 0">{data.mdx.frontmatter.title}</H1>
            <MDXRenderer>{data.mdx.body}</MDXRenderer>
          </Post>
        </Container>
      )
    }
    export const pageQuery = graphql`
      query SinglePostQuery($id: String!, $imagePath: String) {
        mdx(id: { eq: $id }) {
          body
          frontmatter {
            date
            excerpt
            featureImg {
              childImageSharp {
                fixed(width: 1920) {
                  ...GatsbyImageSharpFixed
                }
              }
            }
            title
            slug
          }
        }
        default: file(relativePath: { eq: $imagePath }) {
          publicURL
        }
      }
    `
    export default singlePost
    

    Note: add the $imagePath as a nullable value (by removing the exclamation mark, !), since, as you said, not all the posts will have it.

    Try removing the featureImg block if it stills breaking the query. Because of:

    const imagePath = edge.node.featureImg || 'default.jpg'
    

    Your imagePath variable will always contain the needed data,or your featureImg, or your default one. The key is to make separate queries.