graphqlgatsby

Using frontmatter in a markdownfile to query for images in a page query


I'm using GraphQL from within a Gatsby project. I have a set of markdown files for a blog-like section of the site. In the frontmatter of each markdown file, there's an image property.

What I'd like to do is use Gatsby's fine image api to load the image in the frontmatter. When viewing an individual post (the ones created via createPage api), this works just fine because I can provide the frontmatter.image in the context. Here's what that query looks like.

export const pageQuery = graphql`
  query($slug: String!, $image: String) {
    markdownRemark(frontmatter: { slug: { eq: $slug } }) {
      html
      frontmatter {
        date(formatString: "MMMM DD, YYYY")
        slug
        title
        image
      }
    }
    coverImage: file(relativePath: { eq: $image }) {
        childImageSharp {
          fluid(maxWidth: 1440) {
            ...GatsbyImageSharpFluid
          }
        }
      }
  }
`

On my index page where I'm displaying all these posts though, I want to display a smaller version of this image. I can get the image from the front matter easy enough, but I'm not sure how to integrate that into the query.

export const pageQuery = graphql`
  query {
    allMarkdownRemark(sort: { order: DESC, fields: [frontmatter___date] }) {
      edges {
        node {
          id
          excerpt(pruneLength: 250)
          frontmatter {
            date(formatString: "MMMM DD, YYYY")
            slug
            title
            image # <--- want to use this in a file query
          }
        }
      }
    }
  }
`

As far as I understand, I can't use string interpolation in a static query in the component where the image is actually used, so I need to get it here in the page query. Is what I'm trying to do possible? Is there a better way to handle this?


Solution

  • This "link" between your frontmatter's image string and an actual image file node (processed with Sharp) is called a foreign-key relationship.

    Creating foreign-key relationships in Gatsby

    There are two ways of doing this:

    1. Using mappings in gatsby-config.js
    2. Using a GraphQL @link directive through Gatsby's schema customization (from v2.2)

    I recommend the second option, since it's a more GraphQL way of doing things, and happens in gatsby-node.js where most node operations are taking place. However, if you're starting out with Gatsby and GraphQL, the first option might be easier to set up.

    Implementing the @link directive in gatsby-node.js

    In your case, using the @link GraphQL directive, you would probably end up with something like this in your gatsby-node.js:

    exports.createSchemaCustomization = ({ actions }) => {
      const { createTypes } = actions
      const typeDefs = [
        `type MarkdownRemark implements Node { frontmatter: Frontmatter }`,
        `type Frontmatter {
          # you may need to adapt this line depending on the node type and key
          # that you want to create the relationship for
          image: File @link(by: "relativePath")
        }`
      ]
      createTypes(typeDefs)
    }
    

    Query processed images via your frontmatter

    Finally, you'll be able to query like this:

    {
      allMarkdownRemark {
        edges {
          node {
            frontmatter {
              date
              slug
              title
              # image now points to the image file node
              image {
                childImageSharp {
                  fluid(maxWidth: 1024) {
                    ...GatsbyImageSharpFluid
                  }
                }
              }
            }
          }
        }
      }
    }