July 5, 2024
Load More Button with NextJS 14 and Sanity
Google recently announced they were going to kill the infinite scroll feature on their search pages. That finally motivated to code a load more button feature for the index page. It used to load everything all at once, but now that I have a bit more content, it's good to have this feature for a better overall experience.
What needed to be done:
- Modify my getAllArticles function to accept pagination parameters
- Modify the index page to only display the first 5 articles
- Add a load more button that will be rendered client-side with the following behavior:
- First time rendered: just show the button
- Once pressed, render the PostLoop component with the right article and re-render the button at the bottom
- If no more articles, display some text to tell the user you're at the bottom of the list
Code snippets
"use client";
import React, { useState } from "react";
import PostLoop from "./PostLoop";
import { getAllArticles } from "@/utils/sanity";
import { PostType } from "@/types/post";
const LoadMoreButton = () => {
// State variables to keep track of the current page and the new page of articles
const [currentPage, setCurrentPage] = useState<number>(3);
const [newPage, setNewPage] = useState<PostType[]>([]);
// Function to fetch the next page of articles when the button is clicked
const onClick = async () => {
// Fetch the next page of articles using the getAllArticles function
const newPosts: PostType[] = await getAllArticles({
page: currentPage,
pageSize: 3,
});
// Append the new articles to the existing newPage array
setNewPage([...newPage, ...newPosts]);
// If newPosts is not empty, increment the currentPage by 1
// Otherwise, set the currentPage to 0 to indicate that there are no more articles to load
if (newPosts.length !== 0) {
setCurrentPage(currentPage + 1);
} else {
setCurrentPage(0);
}
};
return (
<>
{/* Render the PostLoop component with the newPage array */}
<PostLoop posts={newPage} />
{/* Render the load more button or the "no more articles" message based on the currentPage */}
<div className="flex flex-col justify-center items-center">
{currentPage !== 0 && (
<button
className="bg-violet-700 text-white py-2 px-4 rounded"
onClick={onClick}
>
Load More Articles
</button>
)}
{currentPage === 0 && (
<h2 className="mb-2 text-2xl text-black">
No more articles to load 😢
</h2>
)}
</div>
</>
);
};
export default LoadMoreButton;
And the updated utility function
const getAllArticles = async ({
page = -1,
pageSize = 0,
}: { page?: number; pageSize?: number } = {}) => {
let skip: number = 0;
let filter: string = "";
if (page > 0 && pageSize > 0) {
skip = (page - 1) * pageSize; // Calculate the number of items to skip
filter = `[${skip}...${skip + pageSize}]`; // Add the filter to the query
}
const items = await mySanityClient.fetch(
`*[_type == "post"]{
title,
_id,
_createdAt,
publishedAt,
description,
body,
mainImage,
"slug": slug.current
} | order(publishedAt desc)${filter}`
);
return items;
};