Search code examples
gatsby

Previous & next page link in Gatsby Blog


I wonder what I'm doing wrong trying to create previous & next page link in my Gatsby website, below is the bug I'm trying to solve.

when I click the previous or next link instead of going to the requested page it creates the below route which is not suppose to do!

that's if i click previous previous

as well as in next next

my code for Gatsby-node.js as below

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

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

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

  const results1 = await graphql(`
    query content {
      allMdx(filter: { frontmatter: { key: { eq: "projects" } } }) {
        nodes {
          frontmatter {
            slug
            title
          }
        }
      }
    }
  `)

  const results2 = await graphql(`
    query content {
      allMdx(
        filter: { frontmatter: { key: { eq: "article" } } }
        sort: { fields: frontmatter___date, order: ASC }
      ) {
        nodes {
          frontmatter {
            slug
            title
          }
        }
      }
    }
  `)

  const projectTemplate = path.resolve(`./src/templates/project-details.js`)
  const blogTemplate = path.resolve(`./src/templates/blog-details.js`)
  if (results1.errors || results2.errors) {
    reporter.panicOnBuild(`Error while running GraphQL query.`)
    return
  }

  const posts = results2.data.allMdx.nodes

  posts.forEach((post, index) => {
    const previous = index === 0 ? null : posts[index - 1]
    const next = index === posts.length - 1 ? null : posts[index + 1]
    createPage({
      path: `/blog/${post.frontmatter.slug}`,
      component: blogTemplate,
      context: {
        slug: post.frontmatter.slug,
        previous,
        next,
      },
    })
  })

  results1.data.allMdx.nodes.forEach(node => {
    createPage({
      path: `/projects/${node.frontmatter.slug}`,
      component: projectTemplate,
      context: { slug: node.frontmatter.slug },
    })
  })
}

and My code for blog posts page as below

import React from "react"
import { graphql, Link } from "gatsby"
import { MDXRenderer } from "gatsby-plugin-mdx"
import { GatsbyImage, getImage } from "gatsby-plugin-image"
import Layout from "../components/Layout"
import * as styles from "../styles/blog-details.module.css"

const BlogPosts = ({ data, pageContext }) => {
  console.log(pageContext)
  const { body } = data.article
  const { title, date, stack, featuredImg } = data.article.frontmatter
  const { previous, next } = pageContext

  return (
    <Layout>
      <div className={styles.details}>
        <h2>{title}</h2>
        <p>{date}</p>
        <p>{stack}</p>
        <div className={styles.featured}>
          <GatsbyImage
            image={getImage(featuredImg.childImageSharp.gatsbyImageData)}
            alt={title}
          />
        </div>
        <article className={styles.body}>
          <MDXRenderer>{body}</MDXRenderer>
        </article>
        {previous && (
          <Link to={previous.frontmatter.slug}>
            <h5>{previous.frontmatter.title}</h5>
          </Link>
        )}
        {next && (
          <Link to={next.frontmatter.slug}>
            <h5>{next.frontmatter.title}</h5>
          </Link>
        )}
      </div>
    </Layout>
  )
}

export default BlogPosts

export const query = graphql`
  query BlogDetails($slug: String) {
    article: mdx(frontmatter: { slug: { eq: $slug }, key: { eq: "article" } }) {
      body
      frontmatter {
        slug
        key
        title
        date
        stack
        featuredImg {
          childImageSharp {
            gatsbyImageData(placeholder: BLURRED, layout: FULL_WIDTH)
          }
        }
      }
    }
  }
`

thanks in advance for your help on this.


Solution

  • If I understood you correctly, the URL that is generated when clicking the previous and next buttons is "concatenating" the slug of the post to the current URL instead of replacing it, isn't it?

    So, instead of creating a URL like localhost:8000/blog/next-article is generating a localhost:8000/blog/current-article/next-article.

    This is because of the way you generate the links. You can spot the links route just by hovering the links to see that the URL of the previous/next article is being added at the end of the URL instead of replacing it.

    This is because you are not adding the correct relativity to the links. Simply use:

    {previous && (
      <Link to={`/blog/${previous.frontmatter.slug}`}>
        <h5>{previous.frontmatter.title}</h5>
      </Link>
    )}
    {next && (
      <Link to={`/blog/${next.frontmatter.slug}`}>
        <h5>{next.frontmatter.title}</h5>
      </Link>
    )}
    

    Starting the to property with a slash (/) fixes the relativity. Because your article is inside the /blog/ page you need to add it to the path.

    This is the way the anchors (<a>) work in HTML as well.