Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Programmatically create multiple types of pages in Gatsby.js

I'm building a website with GatsbyJS. I have markdown files in two different folders: /content/collections and /content/posts and I want Gatsby to create a page for each markdown file, with the respective template (collection.js and post.js).

So I wrote this in my gatsby-node.js file:

const path = require('path');
const { createFilePath } = require('gatsby-source-filesystem');
exports.onCreateNode = ({ node, getNode, actions }) => {
  const { createNodeField } = actions;
  if (node.internal.type === 'MarkdownRemark') {
    const longSlug = createFilePath({ node, getNode, basePath: 'content' });
    const slug = longSlug.split('/');
    createNodeField({
      node,
      name: 'slug',
      value: `/${slug[slug.length - 2]}/`,
    });
  }
};

exports.createPages = async ({ graphql, actions }) => {
  const { createPage } = actions;
  const result = await graphql(`
    query {
      allFile(filter: {relativeDirectory: {eq: "collections"}}) {
        edges {
          node {
            childMarkdownRemark {
              fields {
                slug
              }
            }
          }
        }
      }
    }
  `);
  result.data.allFile.edges.forEach(({ node }) => {
    createPage({
      path: node.childMarkdownRemark.fields.slug,
      component: path.resolve('./src/templates/collection.js'),
      context: {
        slug: node.childMarkdownRemark.fields.slug,
      },
    });
  });
};

exports.createPages = async ({ graphql, actions }) => {
  const { createPage } = actions;
  const result = await graphql(`
    query {
      allFile(filter: {relativeDirectory: {eq: "posts"}}) {
        edges {
          node {
            childMarkdownRemark {
              fields {
                slug
              }
            }
          }
        }
      }
    }
  `);
  result.data.allFile.edges.forEach(({ node }) => {
    createPage({
      path: node.childMarkdownRemark.fields.slug,
      component: path.resolve('./src/templates/post.js'),
      context: {
        slug: node.childMarkdownRemark.fields.slug,
      },
    });
  });
};

Thinking that it would work. It does work for the second type that I put in. (in this case it creates the posts, but not the collections. If I invert the order in which I call createPages it swaps, but it never creates all of them)

This is the error that I get in the console:

warning The GraphQL query in the non-page component "/Users/matteocarpi/Documents/Web/Ledue/src/templates/collection.js" will not be run.
Exported queries are only executed for Page components. It's possible you're
trying to create pages in your gatsby-node.js and that's failing for some
reason.

If the failing component(s) is a regular component and not intended to be a page
component, you generally want to use a <StaticQuery> (https://gatsbyjs.org/docs/static-query)
instead of exporting a page query.

If you're more experienced with GraphQL, you can also export GraphQL
fragments from components and compose the fragments in the Page component
query and pass data down into the child component — https://graphql.org/learn/queries/#fragments

The two templates are very similar:

import React from 'react';

import { graphql } from 'gatsby';
import PropTypes from 'prop-types';

const Post = ({data}) => {
  return (
    <div>
      <h1>{data.postData.frontmatter.title}</h1>
    </div>
  );
};

export default Post;

export const query = graphql`
query PostData($slug: String!) {
  postData: markdownRemark(fields: {slug: {eq: $slug}}) {
    frontmatter {
      title
    }
  }
}
`;

Post.propTypes = {
  data: PropTypes.node,
};
import React from 'react';

import { graphql } from 'gatsby';
import PropTypes from 'prop-types';

const Collection = ({data}) => {
  return (
    <div>
      <h1>{data.collectionData.frontmatter.title}</h1>
    </div>
  );
};

export default Collection;

export const query = graphql`
query CollectionData($slug: String!) {
  collectionData: markdownRemark(fields: {slug: {eq: $slug}}) {
    frontmatter {
      title
    }
  }
}
`;

Collection.propTypes = {
  data: PropTypes.node,
};

I tried refactoring all of the gatsby-node.js file following this answer but I end up in the same situation.

Where am I getting it wrong?

like image 970
Matteo Carpi Avatar asked Oct 29 '20 15:10

Matteo Carpi


People also ask

What are SRC pages in Gatsby?

“src” is a convention for “source code”. /api JavaScript and TypeScript files under src/api become functions automatically with paths based on their file name. Check out the functions guide for more detail. /pages Components under src/pages become pages automatically with paths based on their file name.

How do you make a Gatsby blog?

To build the project, just type gatsby build and this will create all the static assets inside the public folder. This folder is served by Nginx, so you can navigate to it directly by typing http://localhost in the browser. Alternatively, you can use the gatsby develop --host=0.0. 0.0 command.

Should I use Gatsby JS?

Use GatsbyJS for your blog, eCommerce website or any other general static site that's under 500 pages, where you don't expect to serve any kind of dynamic content.


1 Answers

The issue is that you're overriding your first function declaration with the second. A bit like this:

var a = "hello"
a = "world"

Instead you should do all of your querying and call createPage for all pages you want to create in a single function, something like this:

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

  const collections = graphql(`
    query {
      allFile(filter: {relativeDirectory: {eq: "collections"}}) {
        edges {
          node {
            childMarkdownRemark {
              fields {
                slug
              }
            }
          }
        }
      }
    }
  `).then(result => {
    result.data.allFile.edges.forEach(({ node }) => {
      createPage({
        path: node.childMarkdownRemark.fields.slug,
        component: path.resolve('./src/templates/collection.js'),
        context: {
          slug: node.childMarkdownRemark.fields.slug,
        },
      });
    });
  })

  const posts = graphql(`
    query {
      allFile(filter: {relativeDirectory: {eq: "posts"}}) {
        edges {
          node {
            childMarkdownRemark {
              fields {
                slug
              }
            }
          }
        }
      }
    }
  `).then(result => {
    result.data.allFile.edges.forEach(({ node }) => {
      createPage({
        path: node.childMarkdownRemark.fields.slug,
        component: path.resolve('./src/templates/post.js'),
        context: {
          slug: node.childMarkdownRemark.fields.slug,
        },
      });
    });
  })

  return Promise.all([collections, posts])
};
like image 134
coreyward Avatar answered Nov 15 '22 00:11

coreyward