Pagination

On this page, you'll learn how to implement pagination in your Gatsby project.


Pagination is helpful to break up long lists of content within your site. A list of blog posts, for example, could be split up into multiple pages, each displaying a certain number of posts per page.

There are many ways of approaching pagination. Gatsby's official page has its own pagination guide and example. There are also dedicated plugins built by the community entirely dedicated to pagination, for example, gatsby-awesome-pagination.

Here we'll show you one way of doing it using Gatsby Node APIs. We want the homepage to display a list of titles of the existing posts, but we only want to render 3 posts per list, per page.

1. List the pages in gatsby-node

In the gatsby-node.js file, we use the createPages method with our documents of the type Post.

We use a posts.js template to render the homepage with the path '/'. Then we create a variable called numPages and divide the total amount of documents (posts.length) between the number of documents we want on each page, in this case, three.

We loop through numPages and create a path using the index, like this: `/${i + 1}`. Then we pass the limit and skip values to the context.

Copy
// Example gatsby-node.js file

const path = require('path')

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

  const queryData = await graphql(`
    {
      allPrismicPost {
        nodes {
          id
          url
        }
      }
    }
  `)

  const posts = queryData.data.allPrismicPost.nodes
  const numPages = Math.ceil(posts.length / 3)

  // Create the homepage
  createPage({
    path: '/',
    component: path.resolve(__dirname, 'src/templates/posts.js'),
    context: {
      limit: 3,
      skip: 0,
    },
  })

  // Create listing pages
  Array.from({ length: numPages }).forEach((_, i) => {
    createPage({
      path: `/${i + 1}`,
      component: path.resolve(__dirname, 'src/templates/posts.js'),
      context: {
        limit: 3,
        skip: i * 3,
      },
    })
  })
}

2. Write the page query with pagination

In the posts.js file, we create a query that uses the limit and skip values from the context. It will retrieve a title from each post, and the currentPage and pageCount fields.

Then, we import and render the <Pagination /> component (we'll create it in the next step) and pass the pageInfo data to it.

By default, the results will not be sorted in any particular order. You can change that change by using the sort argument.

Copy
// Example posts.js file

import * as React from 'react'
import { graphql } from 'gatsby'
import { RichText } from 'prismic-reactjs'
import { Pagination } from '../components/pagination'

export const query = graphql`
  query MyQuery($limit: Int!, $skip: Int!) {
    allPrismicPost(
      sort: { fields: data___date, order: DESC }
      limit: $limit
      skip: $skip
    ) {
      nodes {
        data {
          title {
            raw
          }
        }
      }
      pageInfo {
        currentPage
        pageCount
      }
    }
  }
`

const Homepage = ({ data }) => {
  if (!data) return null
  const docs = data.allPrismicPost
  return (
    <div>
      {docs.nodes.map((postTitle, i) => {
        return (
          <div key={i}>
            <h1>{RichText.asText(postTitle.data.title.raw)}</h1>
            <hr/>
          </div>
        )
      })}
      <Pagination pageInfo={docs.pageInfo}/>
    </div>
  )
}

export default Homepage

3. Create a pagination component

In the /components folder, create a Pagination.js file. Our component will perform two actions

  1. Render a numeric pagination
  2. Render previous and next buttons that enable and disable when needed

1. Numeric pagination

Retrieve the currentPage and pageCount from the props and then use an Array.from method to loop over the length of pageCount. We'll render each link using Gatsby Link.

The numClass variable will determine the className of the active and inactive links. This will be useful when you add styling to the numeric buttons.

Then, the getPageNumberPath function helps us resolve the correct path for the pagination.

Copy
// Truncated Pagination.js file

import * as React from 'react'
import { Link } from 'gatsby'

// Create URL path for numeric pagination
const getPageNumberPath = (currentIndex) => {
  if (currentIndex === 0) {
    return '/1'
  }
  return '/' + (currentIndex + 1)
}

export const Pagination = ({ pageInfo, path }) => {
  if (!pageInfo) return null
  const { currentPage, pageCount } = pageInfo

  return (
    <div className="pagination">
      {Array.from({ length: pageCount }, (_, i) => {
        let numClass = 'pageNumber'
        if (currentPage === i + 1) {
          numClass = 'currentPage'
        }
        return (
          <Link to={getPageNumberPath(i)} className={numClass} key={i + 1}>
            {i + 1}
          </Link>
        )
      })}
    </div>
  )
}

2. Previous and next buttons

The last step is to add navigation buttons for the previous and next pages.

Create two variables that will determine the correct path for the buttons. prevPagePath: creates a string subtracting the current page minus one, and nextPagePath: creates a string adding up the plus one to the current page.

Then prevClass and nextClass will determine the className of the links.

Copy
// Truncated Pagination.js file

import * as React from 'react'
import { Link } from 'gatsby'

// ...

export const Pagination = ({ pageInfo }) => {
  if (!pageInfo) return null
  const { currentPage, pageCount } = pageInfo

  // Create URL path for previous and next buttons
  const prevPagePath = currentPage - 1 === 1 ? '/1' : '/' + (currentPage - 1).toString()
  const nextPagePath = '/' + (currentPage + 1).toString()

  // Check if page is first or last to disable previous and next buttons
  const prevClass = currentPage === 1 ? 'disabled' : 'enabled'
  const nextClass = currentPage === pageCount ? 'disabled' : 'enabled'

  return (
    <div className="pagination">
      <Link className={prevClass} to={prevPagePath} rel="prev">
        {'<'}
      </Link>
      {/*  Numeric pagination goes here... */}
      <Link className={nextClass} to={nextPagePath} rel="next">
        {'>'}
      </Link>
    </div>
  )
}

Full pagination component

Here's how the component looks with everything in place. Now you can use it in your project and add styles to it to match your project design needs.

Copy
// Example Pagination.js file

import * as React from 'react'
import { Link } from 'gatsby'

// Create URL path for numeric pagination
const getPageNumberPath = (currentIndex) => {
  if (currentIndex === 0) {
    return '/1'
  }
  return '/' + (currentIndex + 1)
}

export const Pagination = ({ pageInfo }) => {
  if (!pageInfo) return null
  const { currentPage, pageCount } = pageInfo

  // Create URL path for previous and next buttons
  const prevPagePath = currentPage - 1 === 1 ? '/1' : '/' + (currentPage - 1).toString()
  const nextPagePath = '/' + (currentPage + 1).toString()

  // Check if page is first or last to disable previous and next buttons
  const prevClass = currentPage === 1 ? 'disabled' : 'enabled'
  const nextClass = currentPage === pageCount ? 'disabled' : 'enabled'

  return (
    <div className="pagination">
      <Link className={prevClass} to={prevPagePath} rel="prev">
        {'<'}
      </Link>
      {/*  Render numeric pagination  */}
      {Array.from({ length: pageCount }, (_, i) => {
        let numClass = 'pageNumber'
        if (currentPage === i + 1) {
          numClass = 'currentPage'
        }
        return (
          <Link to={getPageNumberPath(i)} className={numClass} key={i + 1}>
            {i + 1}
          </Link>
        )
      })}
      <Link className={nextClass} to={nextPagePath} rel="next">
        {'>'}
      </Link>
    </div>
  )
}

Working example

Download the project to have a working example straightway:

BlogSample blog 

Minimalist sample blog with a full-featured editor.


Was this article helpful?
Not really
Yes, Thanks

Can't find what you're looking for? Get in touch with us on our Community Forum.