Web Studio is here! An enhanced experience to make it easier to create, preview, and collaborate on your website contentLearn More
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 Contacts
We 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)
}
}