user11047036
user11047036

Reputation: 11

How to create a second blog template to my Gatsby Site

My Gatsby Site need 2 blog templates:

  1. stories-template.js
  2. products.template.js

I have the stories-template running for my stories, but I am unsure how to tweak and change my existing codes in gatsby-node + products.template.js to make a second (different) template for my products.

I've tried all the solutions and past questions but no luck.

my code in gatsby-node.js:

const path = require('path');

exports.createPages = ({actions, graphql}) => {
const { createPage } = actions

const postTemplate = path.resolve('src/components/stories-template.js');

return graphql(`
{
    allMarkdownRemark {
        edges {
          node {
            html  
            id 
            frontmatter {
              path
              title
              author
              date
            }
          }
        }
      }
}
`).then(res => {
    if(res.errors) {
        return Promise.reject(res.errors)
    }

    res.data.allMarkdownRemark.edges.forEach(({ node }) => {
            createPage({
                path: node.frontmatter.path,
                component: postTemplate,
        })
    })
})
}

my code in stories-template.js:

import React from 'react'
import Layout from '../components/layout'


export default function Template({data}) {
const post = data.markdownRemark

return(<Layout>
    <div>
        <p>Stories</p>
        <br />
        <p>{post.frontmatter.title}</p>

        <div dangerouslySetInnerHTML={{__html: post.html}} />
    </div>
    </Layout>
)
}


export const postQuery = graphql`
query BlogPostByPath($path: String!) {
    markdownRemark(frontmatter: { path: {eq:$path}}){
        html
        frontmatter{
            path
            title
            date
            author
        }
    }
}
`

This works but now I want to create a different template for products in a products-template.js. Right now my products-template is basically just copied and pasted from my stories-template.

I for the life of me can't seem to figure this out.

Upvotes: 1

Views: 520

Answers (2)

Derek Nguyen
Derek Nguyen

Reputation: 11577

@kennethormandy's answer is correct and will help you add a new blog template!

I just want to add a little bit to it: If you have already organized markdown contents for each template into different directories, it'll feel redundant to add a template props to the frontmatter of every content.

Each MarkdownRemark node has a fileAbsolutePath field, which allows you to filter based on where the content comes from.

For example:

exports.createPages = async ({ actions, graphql }) => {
  const { createPage } = actions
  const results = await graphql(`
    {
       allMarkdownRemark {
         edges {
           node {
             fileAbsolutePath
           }
           // ...other useful fields
         }
       }
    }
  `)

  if (results.errors) throw results.errors
  results.data.allMarkdownRemark.edges.forEach(({ node }) => {
    let template
    if (node.fileAbsolutePath.includes('/blogs/')) template = path.resolve('path/to/blog/template')

    createPage({ ... }) // ...etc
  })
}

Upvotes: 0

kennethormandy
kennethormandy

Reputation: 2150

Like the first comment mentioned, more context might be necessary here, but I’ll give this a go. I think the problem is that regardless of the page, you’re telling the createPage function to use the postTemplate template component.

Gatsby doesn’t automatically read the templates in the template directory or anything like that, you need to add the logic for this yourself.

First, you’ll need to require your other template, for example:

const postTemplate = path.resolve('src/components/stories-template.js');
const productsTemplate = path.resolve('src/components/products-template.js');

Then, you need to decide when to use productsTemplate instead of postTemplate here:

createPage({
  path: node.frontmatter.path,
  component: postTemplate,
})

For example, maybe in each Markdown file, you have template YAML frontmatter:

createPage({
  path: node.frontmatter.path,
  component: node.frontmatter.template === 'product' ? productTemplate : postTemplate,
  })

Here’s how I try and approach it in a slightly more generic way on my own site. The URL structure determines the template: if it’s at /journal, it gets the Journal template component. If it’s at /shop, it gets the Shop template component.

This might not be quite enough to drop into your existing site, but hopefully it gets you closer:

const path = require('path')

exports.createPages = ({ graphql, actions }) => {
  const { createPage } = actions

  // I created an object to hold multiple templates.
  // In this case, my blog posts are at /journal and my
  // products are at /shop, so those are the keys I used here.
  // You might have a different system for determining what files
  // should use what template.
  const templates = {
    journal: path.resolve('src/templates/stories-template.js'),
    shop: path.resolve('src/templates/products-template.js'),
  }

  // Query for all Markdown “nodes”
  // Note I’m using a different GraphQL query, so you’ll need to replace this
  // with yours, or see if something with only the minimum requirements like
  // this works for you.
  return graphql(`
    {
      allMarkdownRemark {
        edges {
          node {
            fields {
              slug
            }
          }
        }
      }
    }
  `).then(result => {
    if (result.errors) {
      console.log(result.errors)
      reject(result.errors)
    }

    // Create pages from Markdown files
    result.data.allMarkdownRemark.edges.forEach(edge => {
      let slug = edge.node.fields.slug

      // Get the part of the slug we want, ex. journal
      let slugKey = slug.split('/')[1]

      // If the slug matches a template, use that, otherwise
      // fallback to the default journal template.
      // You could use your own logic here.
      let template = templates[slugKey] || templates['journal']

      createPage({
        path: slug, // required
        component: template,
        context: { slug: slug },
      })
    })
  })
}

I’m sure something with how I use promises could be improved, but otherwise this is working well for me, and gives you a decent way to add in more templates.

Upvotes: 2

Related Questions