Search code examples
gatsbycontentful

How can I display blog posts from Contentful's API using multiple templates when programmatically creating posts in Gatsby


I have been following the beginner tutorial on Gatsby's website and have a question regarding the use of multiple blog post templates.

If there is a need for two or more templates to be used for the blog, for example:

  1. One template with one image and one block of rich text.
  2. One template with several images and several blocks of rich text.

How is it possible to achieve this in Gatsby when programmatically generating pages within the same URL structure, for example:

/blog/post-title-1 (using template 1) /blog/post-title-2 (using template 2)

Thanks.


Solution

  • Of course, it's doable but keep in mind that you need to provide that information (the chosen layout) to your code. If you've followed the tutorial using a File System Route API you'll need to swap to the "classical" version of create dynamic pages, using the gatsby-node.js since that kind of logic needs to be provided somewhere to let Gatsby be aware of that.

    You can just add a Contentful field called layout which in this case can be an integer or something like that, then in your gatsby-node.js:

    const path = require("path")
    
    exports.createPages = async ({ graphql, actions, reporter }) => {
      const { createPage } = actions
    
      const result = await graphql(
        `
              {
                allContentfulBlogPost {
                  edges {
                    node {
                      layout
                      slug
                    }
                  }
                }
              }
        `
      )
    
      if (result.errors) {
        reporter.panicOnBuild(`Error while running GraphQL query.`)
        return
      }
    
    
      const blogPostTemplate1 = path.resolve(`src/templates/blog-post-1.js`)
      const blogPostTemplate2 = path.resolve(`src/templates/blog-post-2.js`)
    
      const posts = result.data.allContentfulBlogPost.edges
      posts.forEach((post, index) => {
        createPage({
          path: `/blog/${post.node.slug}/`,
          component: post.node.layout === "1" ? blogPostTemplate1 : blogPostTemplate2,
          context: {
            slug: post.node.slug
          },
        })
       })
      })
    }
    

    The File System Route API saves you from adding this kind of query + logic for standard approaches in your gatsby-node.js but since it doesn't support filtering (yet) as soon as you need to add some logic you'll need to go back to the "standard" approach.

    Basically, you are getting your Contentful data, querying for the slug and the layout (the new field), and create dynamic pages based on that criteria.

    Note that in the component, you are choosing one template or another based on the layout field using a ternary condition. Of course, tweak it as you wish. Ideally, in complex approaches (more than 2 layouts) it should be a function for readability.

    The rest of your legacy code (your template queries) will work alone since you are providing the slug (as you did before) using the context in:

          context: {
            slug: post.node.slug
          },