Next and Previous Post in Nextjs
An effort to improve a client site
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.
We understand the task right? I also want you to keep something in mind as we go along:
A solution that works, works regardless of where you choose to appy it.
I spent quite a bit of time trying to find a solution that worked. Asking a myriad of people if they knew a way this could be accomplished.
What annoyed me about the whole thing us that most people I asked kept directing me to pagination examples. Whilst I could modify these to possibly find a solution, that was more work than I was willing to do.
Thought Process
Several ways were suggested to solve the problem:
- fetch all blog posts with some metadata. Store this array of blog ids in a global context.
- Store this array of blog ids in a global context.
- Utilize
getStaticProps/getServerSidePropsto get the blog contents. In the UI, have a prev button that links to the id of the previous index from the array you stored in global context. You would do the same for the next button (links to the id of the next index)
This method presented a couple of problems:
-
It entails modifying too much code
-
Remember what I told you to keep in mind - this is will fail based on how you choose fetch the posts.
-
Another way would be to use the data from our query itself to achieve this - say the date - as a reference point, right? That works in theory, but upon trying to implement I was met with new a whole knew set of issues
Consider this code:
export async function PrevNextArticle(published) {
const query = gql`
query PrevandNextArticles($published: DateTime) {
prevArticle: articleCollection(
order: publishedAt_DESC
limit: 1
skip: 1
where: { publishedAt_lte: $published }
) {
items {
title
slug
category {
categorySlug
}
}
}
nextArticle: articleCollection(
order: publishedAt_DESC
limit: 1
skip: 1
where: { publishedAt_gte: $published }
) {
items {
title
slug
category {
categorySlug
}
}
}
}
`;
return graphQLClient.request(query, {
published,
});
}
This method works for previous post, however next post presents oddities:
- Done this way will somehow skip the next post and get the one after.
- If you were to omit the
skipit returns the most recent post regardless of which post you use as a reference point.
Solution
It was the 30th of March, a Wednesday as the notification for whitep4nth3r’s stream showed up. We were doing numbers working to see how switching her site to 11ty had improved its performance.
I bring whitep4nth3r up because she wrote the pagination example I kept getting sent to. (I shouldn’t have said I was using Contentful when I asked really)
I had put the whole conundrum to the side, then I thought to myself, why not ask the person who wrote said pagination article I kept getting sent to.
So I did
She then thought of a place where it was implemented and shared that as well. That code boils down to this:
const ARTICLE_QUERY = `
query {
articleCollection(order: publishedAt_DESC) {
items {
slug
title
excerpt
category {
title
categorySlug
}
series {
title
slug
}
body {
json
}
}
}
}
`;
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],
};
}
The Lesson
Apart from how to implement this I also learnt:
- Don’t be quick to rush to code it yourself, ask people
- Someone somewhere has the answer to the question/implementatio you’re asking for.
And most importantly…
Lazy Perfectionism — MVA
An efficient developer would have struggled their way to a solution, a lazy one would find one that’s already there.
I was lazy, I found a solution. I am wiser for it.
I have since improved on this code and made it more suitable for the client site.
Back to log