reactjsreact-routergatsbygatsby-plugin-mdx

Gatsby Dynamic Routing URL for Generating Layouts


So I generate conditional layouts with this code:

exports.onCreatePage = ({ page, actions }) => {
  const { createPage } = actions;

  if (page.path.match(/about/)) {
    page.context.layout = "special";
    createPage(page);
  }

  if (page.path.match(/projects/)) {
    page.context.layout = "projectsPage";
    createPage(page);
  }
};

I want to change the page.path.mathch(/projects/TO ALL PROJECT SLUGS/) but I can't write the correct syntax for the path.

Does anyone know how to get all the paths after the /projects/ ?

This is the full gatsby.node.js

const path = require("path");
const { createFilePath } = require(`gatsby-source-filesystem`);

exports.onCreatePage = ({ page, actions }) => {
  const { createPage } = actions;

  if (page.path.match(/about/)) {
    page.context.layout = "special";
    createPage(page);
  }

  if (page.path.match(/projects\/([^\/]+$)/)) {
    page.context.layout = "projectsPage";
    createPage(page);
  }
};

exports.onCreateNode = ({ node, getNode, actions }) => {
  const { createNodeField } = actions;
  if (node.internal.type === `MarkdownRemark`) {
    const slug = createFilePath({ node, getNode, basePath: `pages` });
    createNodeField({
      node,
      name: `slug`,
      value: slug,
    });
  }
};

exports.createPages = async function ({ graphql, actions }) {
  const { data } = await graphql(`
    query Projects {
      allMarkdownRemark {
        nodes {
          frontmatter {
            slug
          }
        }
      }
    }
  `);

  data.allMarkdownRemark.nodes.forEach((node) => {
    const slug = node.frontmatter.slug;
    actions.createPage({
      path: "/projects/" + slug,
      component: path.resolve("./src/templates/project-details.js"),
      context: { slug: slug },
    });
  });
};

And this is my template:

import React from "react";
import { AnimatePresence, motion } from "framer-motion";
import Navbar from "../components/Navbar/Navbar";
import Footer from "../components/Footer/Footer";
import FooterAbout from "../components/Footer/FooterAbout";

const duration = 0.5;

const variants = {
  initial: {
    opacity: 0,
  },
  enter: {
    opacity: 1,
    transition: {
      duration: duration,
      delay: duration,
      when: "beforeChildren",
    },
  },
  exit: {
    opacity: 0,
    transition: { duration: duration },
  },
};

export const Layout = ({ children, location, pageContext }) => {
  if (pageContext.layout === "projectsPage") {
    return (
      <main className="bg-black ">
        <AnimatePresence>
          <motion.main
            key={location.pathname}
            variants={variants}
            initial="initial"
            animate="enter"
            exit="exit"
            className="opacity-loader"
          >
            {children}
          </motion.main>
        </AnimatePresence>
      </main>
    );
  }
  if (pageContext.layout === "special") {
    return (
      <main className="bg-black ">
        <Navbar />
        <AnimatePresence>
          <motion.main
            key={location.pathname}
            variants={variants}
            initial="initial"
            animate="enter"
            exit="exit"
            className="opacity-loader"
          >
            {children}
          </motion.main>
        </AnimatePresence>
        <FooterAbout />
      </main>
    );
  }
  return (
    <main className="bg-black ">
      <Navbar />
      <AnimatePresence>
        <motion.main
          key={location.pathname}
          variants={variants}
          initial="initial"
          animate="enter"
          exit="exit"
          className="opacity-loader"
        >
          {children}
        </motion.main>
      </AnimatePresence>
      <Footer />
    </main>
  );
};

export default Layout;

It seems like I am missing something, it accepts the (/projects/) path but not (/projects/([^/]+$)/).

To make it more clear I only want to disable the layout in the subdirectory pages of projects not in the /projects/ page.


Solution

  • You can use the following regular expression:

    projects\/([^\/]+$)
    

    This will match everything after /projects/. So:

      if (page.path.match(/projects\/([^\/]+$)/)) {
        page.context.layout = "projectsPage";
        createPage(page);
      }
    

    I've added a sandbox to test all scenarios: https://regex101.com/r/eQRJb4/1

    Alternatively, you can try gatsby-plugin-create-client-paths what makes exactly the same job automatically. In your case:

    {
      resolve: `gatsby-plugin-create-client-paths`,
      options: { prefixes: [`/projects/*`] },
    },
    

    You can achieve the same result more easily like:

      data.allMarkdownRemark.nodes.forEach((node) => {
        const slug = node.frontmatter.slug;
        actions.createPage({
          path: "/projects/" + slug,
          component: path.resolve("./src/templates/project-details.js"),
          context: { slug: slug, layout: "projectsPage" },
        });
      });