In modern web applications, infinite scrolling and "load more" functionality enhance user experience by seamlessly fetching data as users scroll or click to load more items. This tutorial will guide you through implementing these features in a Next.js application using TypeScript and SWR.
Setting Up the Project
1. Initialize a Next.js Project
First, create a new Next.js project if you haven't already:
npx create-next-app@latest my-nextjs-app --typescript
cd my-nextjs-app
2. Install Necessary Dependencies
Ensure you have SWR installed for data fetching:
npm install swr
Backend Setup
In this example, we'll use the JSONPlaceholder API to fetch posts. Create an API route to handle paginated requests.
/src/app/api/posts/route.ts
import { NextRequest, NextResponse } from "next/server";
// Using JSONPlaceholder for data
export const GET = async (req: NextRequest) => {
try {
const res = await fetch('https://jsonplaceholder.typicode.com/posts');
const allData: any[] = await res.json();
const totalCount = allData.length;
const searchParams = req.nextUrl.searchParams;
const page = Number(searchParams.get('page'));
const dataPerPage = Number(searchParams.get('dataPerPage'));
const data = allData.slice(page * dataPerPage, (page + 1) * dataPerPage);
return NextResponse.json(
{ message: 'Ok', success: true, data: data, totalCount },
{ status: 200 }
);
} catch (error: any) {
return NextResponse.json(
{ message: error?.message, success: false },
{ status: 500 }
);
}
};
Frontend Setup
Create a page to fetch and display the posts with infinite scrolling and "load more" functionality.
/src/app/page.tsx
'use client'
import React, { useState } from 'react';
import useSWRInfinite from 'swr/infinite';
type PostType = {
id: number;
title: string;
userId: number;
body: string;
}
const fetchData = async (url: string) => {
const response = await fetch(url);
return response.json();
}
function HomePage() {
const [dataPerPage, setDataPerPage] = useState(20);
const getKey = (pageIndex: number, previousPageData: any) => {
if (previousPageData && !previousPageData.data?.length) return null; // reached the end
return `/api/posts?page=${pageIndex}&&dataPerPage=${dataPerPage}`;
}
const { data: result, setSize, size, mutate, isValidating, isLoading } = useSWRInfinite(getKey, fetchData);
let posts: PostType[] = [];
let totalCount = 0;
if (result?.length) {
result.forEach(item => {
posts = [...posts, ...item.data || []];
});
totalCount = result[0].totalCount || 0;
}
const handleLoadMore = () => {
setSize(size + 1);
}
if (isLoading) return <p>Loading...</p>;
return (
<div>
<h1>All posts</h1>
{posts?.map((item) => (
<div
key={item.id}
style={{
border: '1px solid grey',
marginTop: 10,
padding: 10
}}
>
<h3>{item.title}</h3>
<div dangerouslySetInnerHTML={{ __html: item.body }} />
</div>
))}
{totalCount > posts.length && <button onClick={handleLoadMore}>
{isValidating ? 'Loading...' : 'Load more'}
</button>}
</div>
);
}
export default HomePage;
Explanation
-
API Route:
- Fetch all posts from the JSONPlaceholder API.
- Use query parameters to handle pagination (
page
anddataPerPage
). - Slice the data array based on the current page and the number of items per page.
-
Frontend Component:
useSWRInfinite
is used to handle infinite loading.- The
getKey
function generates the API endpoint URL based on the current page index. - The
handleLoadMore
function increments the page size to load more data. - Posts are displayed in a simple list with a "Load more" button at the bottom.
Conclusion
By following this guide, you can implement efficient infinite loading and "load more" functionality in your Next.js application using TypeScript and SWR. This enhances the user experience by loading data as needed, reducing initial load times and improving overall performance.
Comments
Loading...