Add pagination to Nuxt Content
Updated by Tom Wells on
Once your nuxt blog grows and your home page gets quite long and busy, it might be time to start looking at pagination. This tutorial will show you how to quickly and easily add pagination to your Nuxt Content blog or website to make it easier to navigate for your readers.
This tutorial will not be covering how to write or display your blog posts.
Requirements for the blog
Before getting into the tutorial as a whole, let's establish some requirements for the blog and what we want to achieve:
- The homepage must be at the usual route ('/').
- Next/previous buttons must only display when the next/previous page exists.
- Nine posts per page to be displayed.
- 404 error fallback (just in case).
We're going to achieve this by requesting ten posts per page (this is to determine whether a "next page" will exist) and remove the last post from the array. I feel this is better (and easier) than counting all posts and creating the number of pages required from the start. It's a more iterative approach.
Page structure
For this tutorial, we'll keep the page structure simple so you can visualise how this works:
pages/
- index.vue
- _slug.vue
- page/
-- _number.vue
To quickly explain, inside the pages
folder, there is:
- The homepage
index.vue
to display your latest posts. - The single post
_slug.vue
template that displays the individual post. - A
page
folder containing the_number.vue
dynamic route for the next page(s) of blog posts.
Since we're using Nuxt Content for generating the blog posts, all your markdown files will live in the content/
folder in your root directory.
Adding a "Next page" button to your homepage
The first thing we want to do is add a button on the homepage when the number of posts exceeds what you want to display (in this case, nine posts will be displayed). For this to work, we'll use the limit
method to, first of all, limit the number of posts retrieved. If we reach this limit, we'll add a "Next page" button.
So, here's the asyncData
function in all its glory:
export default {
async asyncData({ $content }) {
const tenPosts = await $content()
.only(['author', 'createdAt', 'description', 'path', 'title'])
.sortBy('createdAt', 'desc')
.limit(10)
.fetch()
const nextPage = tenPosts.length === 10
const posts = nextPage ? tenPosts.slice(0, -1) : tenPosts
return { nextPage, posts }
}
}
This function is fairly self-explanatory in that we're retrieving ten posts from the content
folder, sorting by the createdAt
date, setting the nextPage
variable to true or false if ten posts exist and if it's true, we remove the last post from the array.
To add the "Next page" button, all that's required is something like this:
<section id="next" v-if="nextPage">
<nuxt-link to="/page/2">
Next page
</nuxt-link>
</section>
That's the homepage finished! This link will only appear if there are more posts than will be displayed on the homepage (hence retrieving ten and not 9).
Creating the pages
Now we're able to move from the homepage to (at least) page 2; it's time to hook up the following pages of posts. To achieve this, we can use both the limit
and skip
methods.
To keep this simple, here is the asyncData
method for the _number.vue
template:
export default {
async asyncData({ $content, params, error }) {
const pageNo = parseInt(params.number)
const tenPosts = await $content()
.only(['author', 'createdAt', 'description', 'path', 'title'])
.sortBy('createdAt', 'desc')
.limit(10)
.skip(9 * (pageNo - 1))
.fetch()
if (!tenPosts.length) {
return error({ statusCode: 404, message: 'No posts found!' })
}
const nextPage = tenPosts.length === 10
const posts = nextPage ? tenPosts.slice(0, -1) : tenPosts
return { nextPage, posts, pageNo }
}
}
In this template, the asyncData
method additionally returns the page number (pageNo
) we're currently on, which is useful in your template or head
method. Here's a quick overview of what's going on here:
- The page number is being retrieved and returned as
pageNo
.parseInt
ensures it's an integer. pageNo
is being used to calculate the number of posts toskip
.- As before, we're limiting the number of posts to ten and sorting by latest-first.
- If no posts are found, we return a 404 error with the message "No posts found" - a useful fallback.
nextPage
boolean is set if there are ten posts. If so, we then remove the last post from the array as before.posts
,pageNo
andnextPage
are returned for use in the page template.
Now you're retrieving your posts for this page, let's conditionally add previous and next page links:
<section id="prev-next">
<nuxt-link :to="prevLink">Prev page</nuxt-link>
<nuxt-link v-if="nextPage" :to="`/page/${pageNo + 1}`">Next page</nuxt-link>
</section>
But wait, what's prevLink
doing? Don't worry; it's not that complicated. It's a simple computed
property to determine whether we need to go back to the homepage or a previous "page":
export default {
computed: {
prevLink() {
return this.pageNo === 2 ? '/' : `/page/${this.pageNo - 1}`
}
}
}
Now, you should have pagination for your posts with a 404 fallback if no posts are found and a homepage at '/'. Perfect.
Wrapping up
There are a few ways to achieve pagination with Nuxt Content. For example, you could count all your posts on the homepage to count the number of pages you need. However, once your blog grows to a large size (100+ posts), this could become cumbersome and slow. This method feels more iterative and works well, producing only the number of pages required without needing to count all your posts.
As you can see, the Nuxt Content module gives you everything you need to create a fully-featured blog. Adding pagination is relatively painless, and can be achieved without the need for a database resulting in a fast, SEO-friendly blog.
If you found this useful or if you have any feedback or questions, feel free to follow or comment on Twitter.