Tutorials
Pagination is the process of dividing items into multiple pages or manageable chunks. When using the List endpoint in our API, the number of items to retrieve from the request using the Take parameter defaults to 10. The number of items to retrieve from the request can be increased to a maximum of 50, however, any more than this will require the use of our Skip parameter which can be used for implementing Pagination.
In this tutorial, we'll be showing you how you can implement Pagination in Next.js on a Content List with the Skip and Take parameters using a Load More button to fetch more results.

In this tutorial, we will be paging through a list of Contacts. Our Contact Content Model consists of the following fields:
Name => Text Field
Position => Text Field
Image => Image Field

We've set up a Page Module with a Linked Content Field that links to our Contact Content List. We will use this Page Module to display our first 4 Contacts, then dynamically load the rest.

In the code for our Contacts Page Module (Contacts.js), we can use the getCustomInitialProps function to call the Agility API and take 4 initial contacts at build time. We will return our 4 contacts and the totalCount for our Contacts Content List, which in this case is 10.
Contacts.getCustomInitialProps = async ({
agility,
languageCode,
}) => {
const api = agility
try {
const contacts = await api.getContentList({
referenceName: 'contacts',
languageCode,
take: 4,
sort: 'properties.itemOrder',
direction: api.types.SortDirections.ASC
})
return {
contacts: contacts.items,
totalCount: contacts.totalCount
}
} catch (err) {
console.log(err)
}
}Next, let's add some code for our React Component. We can destructure customData from the component's props to further destructure contacts and totalCount that we returned from our getCustomInitialProps function.
We can map over our contacts to display our initial results.
import React, { useState } from 'react'
import { AgilityImage } from "@agility/nextjs"
const Contacts = ({ customData }) => {
const { contacts, totalCount } = customData;
const [skip, setSkip] = useState(4)
const [take, setTake] = useState(4);
const [loading, setLoading] = useState(false)
const [loadedContacts, setLoadedContacts] = useState([])
const loadMoreContacts = async () => {
setLoading(true)
setTimeout(() => {
setLoading(false)
}, 500)
}
return (
<div className="max-w-screen-xl mx-auto">
<div className="grid grid-cols-4 gap-8">
{contacts.map((contact, index) => (
<div key={index}>
<div className='relative h-64'>
<AgilityImage
src={contact.fields.image.url}
alt={contact.fields.image.label}
className="object-cover object-center rounded-lg"
layout="fill"
/>
</div>
<p className='text-xl mt-2 font-bold'>{contact.fields.name}</p>
<span className='text-gray-600'>{contact.fields.position}</span>
</div>
))}
</div>
<div className='text-center mt-20'>
<button onClick={loadMoreContacts} className="bg-primary-400 text-white p-4 rounded-lg w-40 mx-auto text-center">
{loading ? 'Loading...' : 'Load More'}
</button>
</div>
</div>
)
}
export default ContactsWe can also set up some state for skip, take, loading, and our loadedContacts.
Lets update our loadMoreContacts function with the following code:
const loadMoreContacts = async () => {
setLoading(true)
setSkip(skip + 4)
setTimeout(async () => {
const { data } = await axios.get(`/api/getContacts?skip=${skip}&take=${take}`)
setLoadedContacts(prevState => [...prevState, ...data.contacts])
setLoading(false)
}, 500)
}true to show some feedback to the userskip to skip + 4, this ensure's that we are always skipping the previous 4 contacts when fetching more500ms that fetches contacts from our /api/getContacts API endpoint, passing in the skip and take values as query parametersfalse.We've created a file in the /pages/api directory called getContacts.js with the following code:
import agility from "@agility/content-fetch"
export default async (req, res) => {
const { skip, take } = req.query
const api = agility.getApi({
guid: process.env.AGILITY_GUID,
apiKey: process.env.AGILITY_API_FETCH_KEY
})
const contacts = await api.getContentList({
referenceName: 'contacts',
languageCode: 'en-us',
take: take,
skip: skip
})
res.status(200).json({ contacts: contacts.items })
};
This sets up an instance of our API using the @agility/content-fetch package. We grab skip & take from the request query parameters and use them to page through our API call and return our contacts.
The final step is to map over the loadedContacts array to display them on the front end. Here's the complete code for our Contacts.js Page Module:
import React, { useState } from 'react'
import { AgilityImage } from "@agility/nextjs"
import axios from "axios"
const Contacts = ({ customData }) => {
const { contacts, totalCount } = customData;
const [skip, setSkip] = useState(4)
const [take, setTake] = useState(4);
const [loading, setLoading] = useState(false)
const [loadedContacts, setLoadedContacts] = useState([])
const loadMoreContacts = async () => {
setLoading(true)
setSkip(skip + 4)
setTimeout(async () => {
const { data } = await axios.get(`/api/getContacts?skip=${skip}&take=${take}`)
setLoadedContacts(prevState => [...prevState, ...data.contacts])
setLoading(false)
}, 500)
}
return (
<div className="max-w-screen-xl mx-auto">
<div className="grid grid-cols-4 gap-8">
{contacts.map((contact, index) => (
<div key={index}>
<div className='relative h-64'>
<AgilityImage
src={contact.fields.image.url}
alt={contact.fields.image.label}
className="object-cover object-center rounded-lg"
layout="fill"
/>
</div>
<p className='text-xl mt-2 font-bold'>{contact.fields.name}</p>
<span className='text-gray-600'>{contact.fields.position}</span>
</div>
))}
{loadedContacts && loadedContacts.map((contact, index) => (
<div key={index}>
<div className='relative h-64'>
<AgilityImage
src={contact.fields.image.url}
alt={contact.fields.image.label}
className="object-cover object-center rounded-lg"
layout="fill"
/>
</div>
<p className='text-xl mt-2 font-bold'>{contact.fields.name}</p>
<span className='text-gray-600'>{contact.fields.position}</span>
</div>
))}
</div>
{totalCount - 4 !== loadedContacts.length && (
<div className='text-center mt-20'>
<button onClick={loadMoreContacts} className="bg-primary-400 text-white p-4 rounded-lg w-40 mx-auto text-center">
{loading ? 'Loading...' : 'Load More'}
</button>
</div>
)}
</div>
)
}
export default Contacts
Contacts.getCustomInitialProps = async ({
agility,
languageCode,
}) => {
const api = agility
try {
const contacts = await api.getContentList({
referenceName: 'contacts',
languageCode,
take: 4,
sort: 'properties.itemOrder',
direction: api.types.SortDirections.ASC
})
return {
contacts: contacts.items,
totalCount: contacts.totalCount
}
} catch (err) {
console.log(err)
}
}