How To Add Pagination To Your Nextjs Web Application

Dolapo Olatunji
5 min readAug 11, 2024

--

In this tutorial, I will explain how to add pagination to your Nextjs application.

Pagination is a method used to divide data into pages and show those pages based on numbering. For instance, if you get thousands of data from an API call or have thousands of data from a folder to display on your web page, showing all the data in a single web page will result in endless scrolling, causing user fatigue. You can divide those data into pages thereby displaying only a few data on a single page, users can then choose to click on a button to go to the next page. This helps improve users experience.

At the end of this tutorial, you should be able to add pagination to your web application.

To begin, we go through the process of installing Nextjs.

npx create-next-app@latest

On installation, you'll see the following prompts:

What is your project named? my-app
Would you like to use TypeScript? No / Yes
Would you like to use ESLint? No / Yes
Would you like to use Tailwind CSS? No / Yes
Would you like your code inside a `src/` directory? No / Yes
Would you like to use App Router? (recommended) No / Yes
Would you like to use Turbopack for `next dev`? No / Yes
Would you like to customize the import alias (`@/*` by default)? No / Yes
What import alias would you like configured? @/*

After the prompts, create-next-app will create a folder with your project name and install the required dependencies.

To proceed, we will be working with this dummy data:

const data = [
"entry 1",
"entry 2",
"entry 3",
"entry 4",
"entry 5",
"entry 6",
"entry 7",
"entry 8",
"entry 9",
"entry 10",
"entry 11",
"entry 12",
"entry 13",
"entry 14",
"entry 15",
"entry 16",
"entry 17",
"entry 18"
]

Using the searchParams we can pass page and perPage into the URL.

export default function pagepagination({ searchParams }:
{
searchParams: { [key: string]: string | string[] | undefined }
}) {

// This includes the page and perPage as part of the url parameter
const page = searchParams['page'] ?? '1';
const perPage = searchParams['perPage'] ?? '3';

return (
<div>pagination</div>
)
}

We use the page page and perPage to calculate the start and end of our entries, and then use them to slice our data.

export default function pagepagination({ searchParams }:
{
searchParams: { [key: string]: string | string[] | undefined }
}) {

// This includes the page and perPage as part of the url parameter
const page = searchParams['page'] ?? '1';
const perPage = searchParams['perPage'] ?? '3';

// This is to get where the data to be displayed starts and end
const start = (Number(page) - 1) * Number(perPage) //0, 3, 6...
const end = start + Number(perPage) //3, 6, 9...

// Define entries the entries to be displayed using the start and end
const entries = data.slice(start, end)

return (
<div>pagination</div>
)
}

We then use the entries to create our component and add some styling to it using Tailwindcss.

return (
<div className="flex flex-col items-center justify-center gap-5 h-screen">
{entries.map((entry) => (
<p key={entry} className="bg-black text-white w-[300px]
py-5 text-center uppercase"
>
{entry}
</p>
))}
</div>
)

In the next step, we will be creating a button that helps us navigate between those pages. For that, we will be creating a pagination button component in a separate file.

'use client'

import { useSearchParams, useRouter } from "next/navigation";

export default function PaginationBtn() {

const router = useRouter();
const searchParams = useSearchParams()

const page = searchParams.get('page') ?? '1'
const perPage = searchParams.get('perPage') ?? '3'

return (
<div className="flex
items-center justify-center gap-4">
<button className="bg-black text-white p-2" onClick={() => {
router.push(`/?page=${Number(page) - 1}&per_page=${perPage}`)
}}>
prev
</button>
<div>
{page}/{Math.ceil(18 / Number(perPage))}
</div>
<button className="bg-black text-white p-2" onClick={() => {
router.push(`/?page=${Number(page) + 1}&per_page=${perPage}`)
}}>
next
</button>
</div>
)
}

After creating the buttons, we will realise that the pages can go below 1 i.e. (0,-1,-2) and above the number of total pages. What we will do is add a boolean property of hasNextPageand hasPrevPage to our button. This is to disable the button when it goes below 1 and above the total number of pages.

'use client'

import { useSearchParams, useRouter } from "next/navigation";

export default function PaginationBtn({ hasNextPage, hasPreviousPage }: { hasNextPage: boolean, hasPreviousPage: boolean }) {

const router = useRouter();
const searchParams = useSearchParams()

const page = searchParams.get('page') ?? '1'
const perPage = searchParams.get('perPage') ?? '3'

return (
<div className="flex
items-center justify-center gap-4">
<button disabled={!hasPreviousPage} className="bg-black text-white p-2" onClick={() => {
router.push(`/?page=${Number(page) - 1}&per_page=${perPage}`)
}}>
prev
</button>
<div>
{page}/{Math.ceil(18 / Number(perPage))}
</div>
<button disabled={!hasNextPage} className="bg-black text-white p-2" onClick={() => {
router.push(`/?page=${Number(page) + 1}&per_page=${perPage}`)
}}>
next
</button>
</div>
)
}

In the page file, we will add the PaginationBtn component and add hasNextPage and hasPrevPage as a prop

 return (
<div className="flex flex-col items-center justify-center gap-5 h-screen">
{entries.map((entry) => (
<p key={entry} className="bg-black text-white w-[300px] py-5 text-center uppercase">{entry}</p>
))}
<PaginationBtn hasNextPage={end < data.length} hasPreviousPage={start > 0} />
</div>
)

In conclusion, this is what the page file looks like


const data = [
"entry 1",
"entry 2",
"entry 3",
"entry 4",
"entry 5",
"entry 6",
"entry 7",
"entry 8",
"entry 9",
"entry 10",
"entry 11",
"entry 12",
"entry 13",
"entry 14",
"entry 15",
"entry 16",
"entry 17",
"entry 18"
]

export default function Page({ searchParams }:
{
searchParams: { [key: string]: string | string[] | undefined }
}) {

// This includes the page and perPage as part of the url parameter
const page = searchParams['page'] ?? '1';
const perPage = searchParams['perPage'] ?? '3';

// This is to get where the page starts and end
const start = (Number(page) - 1) * Number(perPage) //0, 3, 6...
const end = start + Number(perPage) //3, 6, 9...

// Define the start to end entries
const entries = data.slice(start, end)

return (
<div className="flex flex-col items-center justify-center gap-5 h-screen">
{entries.map((entry) => (
<p key={entry} className="bg-black text-white w-[300px] py-5 text-center uppercase">{entry}</p>
))}
<PaginationBtn hasNextPage={end < data.length} hasPreviousPage={start > 0} />
</div>
)
}

This is what the paginationbtnfile looks like.

'use client'

import { useSearchParams, useRouter } from "next/navigation";

export default function PaginationBtn(
{ hasNextPage, hasPreviousPage }:
{ hasNextPage: boolean, hasPreviousPage: boolean }
) {

const router = useRouter();
const searchParams = useSearchParams()

const page = searchParams.get('page') ?? '1'
const perPage = searchParams.get('perPage') ?? '3'

return (
<div className="flex
items-center justify-center gap-4">
<button disabled={!hasPreviousPage} className="bg-black text-white p-2" onClick={() => {
router.push(`/?page=${Number(page) - 1}&per_page=${perPage}`)
}}>
prev
</button>
<div>
{page}/{Math.ceil(18 / Number(perPage))}
</div>
<button disabled={!hasNextPage} className="bg-black text-white p-2" onClick={() => {
router.push(`/?page=${Number(page) + 1}&per_page=${perPage}`)
}}>
next
</button>
</div>
)
}

If you find this tutorial helpful, kindly give it a like. Thank you.

--

--