DEV Community

Cover image for Previous and Next Post using Next.js
James 'Dante' Midzi
James 'Dante' Midzi

Posted on

Previous and Next Post using Next.js

I spent weeks trying to figure this out, I am not going to make you go through the same. Let's get straight into it...

The Problem

On a Next.js site with posts fetched via graphql, how would you implement Previous and Next POST. This would be on the detail view of a post.

The Solution

The solution below uses graphql and Contentful but can be modified to wherever you want to apply it.

const ARTICLE_QUERY = `
query {
    articleCollection(order: publishedAt_DESC) {
      items {
        slug
        title
        excerpt
        category {
          title
          categorySlug
        }
        series {
          title
          slug
        }
        body {
          json
        }
      }
    }
  }

`;
Enter fullscreen mode Exit fullscreen mode
async function fetchGraphQL(query) {
  return fetch(
    `https://graphql.contentful.com/content/v1/spaces/${process.env.SPACE_ID}`,
    {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        Authorization: `Bearer ${process.env.CDA_TOKEN}`,
      },
      body: JSON.stringify({ query }),
    }
  )
    .then((response) => response.json())
    .catch((error) => console.error(error));
}

export async function getPostAndMorePosts(slug) {
  const response = await fetchGraphQL(ARTICLE_QUERY);
  const posts = response.data.articleCollection.items;

  const currentPost = posts.find((post) => post.slug === slug);
  const currentPostIndex = posts.findIndex((post) => post.slug === slug);
  const prevPost = posts[currentPostIndex - 1] || posts[posts.length - 1];
  const nextPost = posts[currentPostIndex + 1] || posts[0];

  if (!currentPost) {
    return {
      post: false,
    };
  }

  return {
    post: currentPost,
    morePosts: [prevPost, nextPost],
  };
}
Enter fullscreen mode Exit fullscreen mode

In the code above:

  • We create the article query and make a function to perform our query - fetchGraphQL
  • We then export an async function getPostAndMorePosts that takes slug as a parameter. In it, we use fetchGraphQL to get all our posts as an array.
  • We use find to get the post with the slug that matches the passed slug parameter - currentPost
  • Using currentPost as an index, we get the post before and the post after - these become our previous post and next post respectively
  • Lastly, return currentPost as post and morePosts as an array of the previous and next post.

in [slug].js:

import getPostAndMorePosts from '../../lib/api'

export async function getStaticPaths() {
  ...
}

export async function getStaticProps({ params }) {
  const data = await getPostAndMorePosts(params.slug);

  return {
    props: {
      post: data?.post ?? null,
      morePosts: data?.morePosts ?? null,
    },
  };
}

export default function Post({ post, morePosts }) {
    ...
}
Enter fullscreen mode Exit fullscreen mode

In our dynamic page:

  • We import getPostAndMorePosts - we will use it in getStaticProps after we pass the params from getStaticPaths.
  • In getStaticProps, we pass params.slug to getPostAndMorePosts to get:
    • The current post data
    • The previous and next post data
  • Lastly, we pass this data to our component and render our data.

Here is the repo with the full implementation:
https://github.com/stefanjudis/jamstack-workshop/blob/main/lib/api.js


Conclusion

Thank you whitep4nth3r, Thank you to everyone who tried to help.

The full story of how we got here is coming in another article. It really puzzles me how there isn't a native solution for this. Gatsby has it, WordPress does as well. What happened here Next.js?


Thank you for reading, let's connect!

Thank you for visiting this little corner of mine. Let's connect on Twitter, Polywork and LinkedIn)

Top comments (3)

Collapse
 
vaggelis_best profile image
vaggelis best

Hi,
Though I neither know a lot about JS nor GraphQL I would assume that the following code mentioned in the article at getPostAndMorePosts(slug)

const currentPost = posts.find((post) => post.slug === slug);
  const currentPostIndex = posts.findIndex((post) => post.slug === slug);

Enter fullscreen mode Exit fullscreen mode

Could be mildly optimised by doing the following:

  const currentPostIndex = posts.findIndex((post) => post.slug === slug);
const currentPost = posts[currentPostIndex];

Enter fullscreen mode Exit fullscreen mode

My assumption is that running a find function only once instead of twice can be quite beneficial in terms of performance, especially if using someting like Next.js' ISR on build time for each post of a grant list...
Am I right or wrong?

Collapse
 
psypher1 profile image
James 'Dante' Midzi

You're not mistaken. You would not be wrong in any other situation.

If I remember correctly, it's done this because of how Contentful's graphql works (it's different from most others) and how the function would work in the getStaticProps - but that was an oddity in that version of Nextjs.

Also, something weird happened if you chose to find the index first before the post - mainly because of how the content was structured and needed to be displayed in the sites.

But that's a very good catch! Thank you for reminding me of this!

Collapse
 
vaggelis_best profile image
vaggelis best

Thank YOU for answering! Have a nice day!