I want to go to the previous post & next post from a particular blog post.
Consider, this Gatsby example - /
It has 2 links at the bottom of the page -
← Hello World which is a Previous Post & New Beginnings → which is a Next Post
To use this dynamically, I need to access slug of the post but how would I get that? It's simple in Gatsby due to the use of GraphQL.
But, in Next, I have to use preval hack only to get posts. How will I get slugs so that I can implement Previous and Next Post in my particular blog?
Even Next JS's own blog does not have Next Post and Previous Post links.
Check out any post on Next JS's Blog and you will find a Back to Blog button but no previous post or next post button.
I want to go to the previous post & next post from a particular blog post.
Consider, this Gatsby example - https://gatsby-starter-blog-demolify./my-second-post/
It has 2 links at the bottom of the page -
← Hello World which is a Previous Post & New Beginnings → which is a Next Post
To use this dynamically, I need to access slug of the post but how would I get that? It's simple in Gatsby due to the use of GraphQL.
But, in Next, I have to use preval hack only to get posts. How will I get slugs so that I can implement Previous and Next Post in my particular blog?
Even Next JS's own blog does not have Next Post and Previous Post links.
Check out any post on Next JS's Blog and you will find a Back to Blog button but no previous post or next post button.
Share Improve this question edited Feb 22, 2019 at 7:00 giraffesyo 5,3721 gold badge31 silver badges41 bronze badges asked Feb 21, 2019 at 15:24 deadcoder0904deadcoder0904 8,74318 gold badges86 silver badges208 bronze badges 3- I guess I still don't get your problem... let's say you have a page/route "/blog?articleId=123", and given you have the id's of previous and next article, then you can place a two links to "/blog?articleId=prevId" and "/blog?articleId=nextId", isn/t it. Or do are your blog articles actual pages inside dir "/pages/article1", ..., "/pages/articleN"? – lipp Commented Feb 22, 2019 at 8:13
- I mean you seem to have a static blog which is not using an API for fetching the articles... correct? – lipp Commented Feb 22, 2019 at 8:22
-
Yes, it's a static blog living on my filesystem. It will spit out a static site which will have URLs like
/blog/my-new-post
,/blog/another-post
, etc.. & not like you said in the ment above. That's my question :) – deadcoder0904 Commented Feb 22, 2019 at 14:21
4 Answers
Reset to default 3First, you have to ask yourself what "previous post" and "next post" means and how you find those.
Are you able to express that? Answering this question will solve most of your problem and is not a Next.js related one.
- If you are using incremental IDs, the answer might be "my previous post is my current post's id minus 1" and "plus 1" for the next post.
- If not, the answer might be "my previous post is the post right before the current post by created_at date"
- etc.
Once you have clarified this, the answer to the question "how do I add a previous and next post link" is a mix of what mrvdot and lipp offered.
Create a getNextPost
and getPreviousPost
function. Assuming you have incremental IDs, a simple (and not robust) example would be:
const getPreviousPost = currentPostId => getPost(currentPostId - 1)
const getNextPost = currentPostId => getPost(currentPostId + 1)
Then once you have that, you can query for your previous and next posts in addition of your current post in getInitialProps
of your Post ponent
class Post extends React.Component {
static async getInitialProps ({id}) {
const post = await getPost(id)
const previousPost = await getPreviousPost(id)
const nextPost = await getNextPost(id)
return {
post,
previousPost,
nextPost,
}
}
getPreviousPost = currentPostId => getPost(currentPostId - 1)
getNextPost = currentPostId => getPost(currentPostId + 1)
render ({post, previousPost, nextPost}) {
return (
<div>
<h1>{post.title}</h1>
<p>{post.content}</p>
<Link href={previousPost.url}><a>Previous post</a></Link>
<Link href={nextPost.url}><a>Next post</a></Link>
</div>
)
}
}
Or something like that. Does that make sense?
Because you're generating a static site, I'll assume you only care about figuring this out from the server side of Next.JS. For that, you'd basically need to write a function called getNextPost(current)
that takes your current post (either just the filename or a full object) and scans your posts directory for the next one. For the server side of Next.JS, you're in Node, so you can just use the fs
package to scan the filesystem.
Ideally, that means each filename should have a timestamp or similar as its prefix (e.g. 2019-02-22_13-45-53-my-second-post.html
), that way you can just use a simple sort to determine what the next post is. Otherwise, you'd need to actually parse each post file to pull out the date information that way (I believe that's how Gatsby works, it for sure is what Hugo does).
Either way, I'd remend parsing all your post files into an in-memory cache once so that you don't have to scan the posts directory every time.
Pagination is rather simple. You can use the static async getInitialProps
method, to get the requested page from the query.
Then you can either use a button
with Router.push
to router imperatively or a Link
to route declaratively.
class PaginationTest extends React.Component {
static async getInitialProps ({query: {page = 1}}) {
const data = await fetchPage(page)
return {
page: parseInt(page, 10),
data
}
}
render () {
return (
<div>
// render data
<button
onClick={() => Router.push(`/page=${this.props.page + 1}`)}
disabled={this.props.page <= 1}
>Next Button</button>
<Link href={`/page=${this.props.page + 1}`}>
<a>Next Link</a>
</Link>
</div>
)
}
}
Here is a working codesandbox: https://codesandbox.io/s/6w3k5yzqmn
I had the same problem. I have a function that gets all post headers (i.e. the slug and the frontmatter), and I have the slug of the current post. To find the previous and the next post, I wrote this piece of Typescript:
export interface AdjacentPosts {
previous: { slug: string, title: string } | null,
next: { slug: string, title: string } | null,
};
export function getAdjacentPosts(slug: string): AdjacentPosts {
const allPostHeaders = getAllPostHeaders();
const postIndex = allPostHeaders.findIndex(postHeader => postHeader?.slug === slug);
return {
previous: postIndex <= 0
? null
: { slug: allPostHeaders[postIndex - 1]!.slug, title: allPostHeaders[postIndex - 1]!.frontmatter.title },
next: postIndex >= allPostHeaders.length - 1
? null
: { slug: allPostHeaders[postIndex + 1]!.slug, title: allPostHeaders[postIndex + 1]!.frontmatter.title }
};
}
I use this function in [slug].tsx
:
export const getStaticPaths: GetStaticPaths = async () => {
const slugs = getAllPostSlugs().concat(getAllPageSlugs());
return {
paths: slugs.map(slug => ({ params: { slug }, })),
fallback: false,
}
}
export const getStaticProps: GetStaticProps = async ({ params }) => {
if (!params) {
return { props: {} };
}
const post = getPostBySlug(params.slug as string)
if (post) {
return {
props: {
post,
isPost: true,
adjacentPosts: getAdjacentPosts(post.slug)
}
}
}
const page = getPageBySlug(params.slug as string)
return { props: { post: page!, isPost: false, adjacentPosts: null } };
}
export default function PostBody(props: { post: PostData, isPost: boolean, adjacentPosts: AdjacentPosts | null }) {
const { post, isPost } = props;
const previous = props.adjacentPosts?.previous;
const next = props.adjacentPosts?.next;
return (
<React.Fragment>
...the blog post itself, and then this:
<nav className="post-navigation" role="navigation" aria-label="Posts">
<h2 className="sr-only">Post navigation</h2>
<div className="grid sm:grid-cols-1 md:grid-cols-2 gap-6">
<div className="nav-previous">
{previous && (
<Link href={previous.slug}>
<a className="flex flex-col text-left" rel="prev">
<span className="text-base mb-2">Previous: </span>
<span className="sr-only">Previous post:</span>
<span className="text-primary-500 leading-6">
{previous.title}
</span>
</a>
</Link>
)}
</div>
<div className="nav-next">
{next && (
<Link href={next.slug}>
<a className="flex flex-col text-right"
rel="next">
<span className="text-base mb-2">Next: </span>
<span className="sr-only">Next post:</span>
<span className="text-primary-500 leading-6">
{next.title}
</span>
</a>
</Link>
)}
</div>
</div>
</nav>
</React.Fragment>
)
}
Good luck with this!