Search code examples
reactjsgraphqlmarkdowngatsby

gatsby multiple collection routes for multiple markdown folders


I have two markdown collection routes which I want to apply to two different sets of markdowns separated by subfolders.

My folder structure is as follows

appfolder
  content
    projects
      project1.md
      project2.md
    article
      article1.md
      article2.md
  src
    pages
      projects
        {MarkdownRemark.frontmatter__slug}.js 
      articles
        {MarkdownRemark.frontmatter__slug}.js 

The content of projects/{MarkdownRemark.frontmatter__slug}.js is as follows

import React from 'react';
import Layout from "../../components/Layout";
import Nav from "../../components/Nav";
import PageHeader from "../../components/PageHeader";
import Footer from "../../components/Footer";
import SimpleReactLightbox from 'simple-react-lightbox'
import { graphql } from 'gatsby'
const ProjectPage = ({data}) => {
    const fm = data.markdownRemark.frontmatter;
    const html = data.markdownRemark.html;
    return (
        <SimpleReactLightbox>
        <Layout pageTitle={fm.title}>
            <Nav />
            <PageHeader title={fm.title} />
            <Footer />
        </Layout>
        </SimpleReactLightbox>
    );
};


export const query = graphql`
query($id: String!) {
    markdownRemark(id: { eq: $id },fileAbsolutePath: {regex: "/(projects)/" }) {
      html
      frontmatter {
        slug
        title
        summary
        icon
      }
    }
}
`

export default ProjectPage;

But GraphiQL shows that the pages are generated for all md files. How do I restrict each collection route to respective subfolder.


Solution

  • I just ran into the same problem. It seems like there are no ready-made solutions for your current organization of src/pages. But after running through the discussions on Gatsby, I found there are some workarounds, though I think it's a bit inconvenient.

    Like this comment said:

    If your source nodes contain something like category: "article" | "blog", you could generate separate routes with file nested in category directory like /{SourceNode.category}/{SourceNode.slug}.tsx.

    If your source node does not contain such a category field, then you can append it to the nodes in onCreateNode hook, or create entirely new nodes for your purposes

    In your case, if you can add a category field to the frontmatter in your .md files, you can change your organization of src to

    appfolder
      content
        projects
          project1.md
          project2.md
        article
          article1.md
          article2.md
      src
        templates
          project-template.js
          article-template.js
        pages
          {MarkdownRemark.frontmatter__category}
            {MarkdownRemark.frontmatter__slug}.js
    

    Then projects/{MarkdownRemark.frontmatter__slug}.js goes to templates/project-template.js and the article one is similar.

    Now in your {MarkdownRemark.frontmatter__slug}.js file, you can forward the data queried by GraphQL to the corresponding template.

    // {MarkdownRemark.frontmatter__slug}.js
    const GeneratedPage = ({ data }) => {
      const templates = {
        project: <ProjectTemplate data={data} />,
        article: <ArticleTemplate data={data} />,
      };
    
      return templates[data.MarkdownRemark.frontmatter.category];
    };
    

    If it's not possible to add the category field into the .md file, make use of the createNodeField API to programmatically add it to MarkdownRemark nodes in gatsby-node.js. It maybe something like this:

    // gatsby-node.js
    exports.onCreateNode = ({ node, actions, getNode }) => {
      const { createNodeField } = actions;
      if (node.internal.type === `MarkdownRemark`) {
        const value = ...; // something mapping the directory of node to certain catecory
        createNodeField({
          name: 'category',
          node,
          value,
        });
      }
    };
    

    Hope this can be helpful to you.