How to implement infinite scrolling and pagination with Next.js and TanStack Query

Paging and infinite scrolling are two popular techniques you can use to optimize application performance. They can help you process existing data more efficiently and enhance the overall user experience.

Most applications you develop will need data management. As programs continue to scale, the amount of data that needs to be managed is increasing. When an app fails to manage data effectively, it performs poorly.

How to implement infinite scrolling and pagination with Next.js and TanStack Query Picture 1How to implement infinite scrolling and pagination with Next.js and TanStack Query Picture 1

Paging and infinite scrolling are two popular techniques you can use to optimize application performance. They can help you process existing data more efficiently and enhance the overall user experience.

Pagination and endless scrolling with TanStack Query

TanStack Query - an adaptation of React Query - is a powerful state management library for JavaScript applications. It provides an effective solution for managing application state among other functions, including data-related tasks such as caching.

How to implement infinite scrolling and pagination with Next.js and TanStack Query Picture 2How to implement infinite scrolling and pagination with Next.js and TanStack Query Picture 2

Pagination involves dividing a large group of data into small pages, allowing users to navigate content in manageable groups using navigation buttons. In contrast, infinite scrolling provides a more dynamic browsing experience. As users scroll, new data is loaded and displayed automatically, eliminating the need for explicit navigation.

Paging and infinite scrolling aim to effectively manage and display large data. Choosing between the two depends on the application's data requirements.

Set up the Next.js project

To get started, create a Next.js project. Install the latest version of Next.js 13 which uses the App folder.

npx create-next-app@latest next-project --app

Next, install the TanStack package in the project using npm, the Node.

npm i @tanstack/react-query

Integrate TanStack query in Next.js app

To integrate TanStack Query in your Next.js project, you need to create and initialize a new instance of TanStack Query in your application's root directory - the layout.js file . To do that, import QueryClient and QueryClientProvider from the TanStack query . Then include the child property with the QueryClientProvider as follows:

"use client" import React from 'react' import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; const metadata = { title: 'Create Next App', description: 'Generated by create next app', }; export default function RootLayout({ children }) { const queryClient = new QueryClient(); return ( {children} ); } export { metadata };

This setting ensures that the TanStack Query has completed accessing the application state.

Implement pagination using the useQuery hook

The useQuery hook fetches and manages data. By providing pagination parameters, such as page numbering, you easily access specific sub-groups of data.

Additionally, this hook provides various options and configurations to customize data fetching functionality, including setting cache options, as well as handling load state efficiently. With these features, you can create an effective pagination experience.

Now to implement pagination in the Next.js app, create the file Pagination/page.js in the src/app directory . Inside this file, create the following imports :

"use client" import React, { useState } from 'react'; import { useQuery} from '@tanstack/react-query'; import './page.styles.css';

Then, define the React function component. Inside this component, you need to define a function that fetches data from the external API. In this case, use the JSONPlaceholder API to fetch a group of posts.

export default function Pagination() { const [page, setPage] = useState(1); const fetchPosts = async () => { try { const response = await fetch(`https://jsonplaceholder.typicode.com/posts? _page=${page}&_limit=10`); if (!response.ok) { throw new Error('Failed to fetch posts'); } const data = await response.json(); return data; } catch (error) { console.error(error); throw error; } }; // thêm code sau vào đây }

Now define the useQuery hook, and define the following parameters as objects:

 const { isLoading, isError, error, data } = useQuery({ keepPreviousData: true, queryKey: ['posts', page], queryFn: fetchPosts, });

The keepPreviousData value is true , which ensures that while fetching new data, the app will keep the previous data intact. The queryKey parameter is an array containing the key for this query, so the endpoint and current page are where you want to fetch data for them. Finally, the queryFn parameter , fetchPosts , triggers a function call to fetch the data.

As mentioned initially, this hook provides a number of states that you can open, similar to how you would deconstruct arrays and objects, and then use them to improve the user experience (show proper UI ) while performing the data fetch. These states include isLoading , isError , and more.

To do that, include the following code to show different notification screens based on the current state of the process in progress:

 if (isLoading) { return (

Loading.

); } if (isError) { return (

{error.message}

); }

Finally, include this code for the JSX elements that appear on the browser page. This code also serves two different functions:

  1. After this app fetches posts from the API, they will be in the data variable provided by the useQuery hook . This variable helps manage the state of the application. You can then 'map' the list of posts stored in this variable, and then display them in the browser.
  2. To add two navigation buttons, Previous and Next , allow users to query and show additional paginated data respectively.
 return (

Next.js Pagination

 {data && (

{data.map((post) => (

    1. {post.title}

))}

 )}   );

Finally, start the programming server.

npm run dev

Then go to http://localhost:3000/Pagination in a browser.

How to implement infinite scrolling and pagination with Next.js and TanStack Query Picture 3How to implement infinite scrolling and pagination with Next.js and TanStack Query Picture 3

Because you included the Pagination folder in the app folder , Next.js sees it as a route, allowing you to access the page using that URL.

Infinite scrolling using the useInfiniteQuery hook

Infinite scrolling provides a seamless browsing experience. YouTube is an obvious example, it automatically fetches new videos and shows them as you scroll the page.

The useInfiniteQuery hook allows you to implement infinite scrolling by fetching data from a server within pages and automatically fetching and displaying the next page of data when the user scrolls down.

To implement infinite scrolling, add the InfiniteScroll/page.js file in the src/app directory . Then, do the following imports:

"use client" import React, { useRef, useEffect, useState } from 'react'; import { useInfiniteQuery } from '@tanstack/react-query'; import './page.styles.css';

Next, create the React function component. Inside this component, similar to pagination, create a function to fetch the article's data.

export default function InfiniteScroll() { const listRef = useRef(null); const [isLoadingMore, setIsLoadingMore] = useState(false); const fetchPosts = async ({ pageParam = 1 }) => { try { const response = await fetch(`https://jsonplaceholder.typicode.com/posts? _page=${pageParam}&_limit=5`); if (!response.ok) { throw new Error('Failed to fetch posts'); } const data = await response.json(); await new Promise((resolve) => setTimeout(resolve, 2000)); return data; } catch (error) { console.error(error); throw error; } }; // thêm code sau vào đây }

Unlike paging, this code introduces a 2 second delay when fetching data to allow the user to explore the current data while scrolling to trigger re-fetching of a new set of data.

Now, define the useInfiniteQuery hook. When mounting the initial component, this hook will fetch the first page of data from the server. When the user scrolls down, this hook will automatically fetch the next page of data and display it in this element.

 

 const { data, fetchNextPage, hasNextPage, isFetching } = useInfiniteQuery({ queryKey: ['posts'], queryFn: fetchPosts, getNextPageParam: (lastPage, allPages) => { if (lastPage.length < 5) { return undefined; } return allPages.length + 1; }, }); const posts = data ? data.pages.flatMap((page) => page) : [];

The posts variable combines all the posts from different pages into one array, giving a simplified version of the data variable. This allows you to easily map and display individual posts.

To track user scrolling and load more data as the user nears the bottom of the list, you can define a function that uses the Intersection Observer API to detect when elements intersect the frame.

 const handleIntersection = (entries) => { if (entries[0].isIntersecting && hasNextPage && !isFetching && !isLoadingMore) { setIsLoadingMore(true); fetchNextPage(); } }; useEffect(() => { const observer = new IntersectionObserver(handleIntersection, { threshold: 0.1 }); if (listRef.current) { observer.observe(listRef.current); } return () => { if (listRef.current) { observer.unobserve(listRef.current); } }; }, [listRef, handleIntersection]); useEffect(() => { if (!isFetching) { setIsLoadingMore(false); } }, [isFetching]);

Finally, include JSX components for the article to appear in the browser.

 return (

Infinite Scroll

{posts.map((post) => (

    1. {post.title}

))}

 {isFetching ? 'Fetching.' : isLoadingMore ? 'Loading more.' : null} );

After you have made all the necessary changes, go to http://localhost:3000/InfiniteScroll to see them in action.

It's done! Hope the article is useful to you.

4 ★ | 1 Vote