Infinite scrolling is probably one of the most widely used features in today's web applications. Rather than relying on traditional pagination, it loads further content dynamically when users scroll. In this tutorial, you will learn how to implement a basic and simple Infinite Scroll component in React JS by fetching GitHub users from the GitHub API.π―
ππ° Perfect for beginners! This guide focuses on the core logic without overwhelming you with complex React patterns.
π―What We Will Build
We will build a clean React application showing GitHub users as cards. When users scroll, additional users will be fetched automatically, providing an overall smooth browsing experience.
π Tech Stack:
βοΈ useState
to manage state
βοΈ useEffect
to handle side effects like API calls and event listeners
π fetch API
to load data
π Step-by-Step Implementation
1οΈβ£ Project Setup and Initial Imports
Ensure your React project is set up.
π Tools like Vite β‘οΈ make the process super fast.
Make a components
directory inside the src
folder, and create 2 files in it:
InfiniteScroll.jsx
InfiniteScroll.css
Import Essentials:
Open InfiniteScroll.jsx
file and start with the following imports.
import React, { useEffect, useState } from 'react'
import './InfiniteScroll.css'
In the given code snippet above, we are importing useState
and useEffect
from React
, and we are importing a CSS file to style the user cards later.
2οΈβ£ States Configuration
We shall manage three primary pieces of state:
π₯ users
β stores the fetched users.
π page
β track the current page.
π isLoading
β prevents multiple simultaneous API calls.
const [users, setUsers] = useState([]);
const [page, setPage] = useState(1);
const [isLoading, setIsLoading] = useState(false);
3οΈβ£ Fetch Users from GitHub API
We are creating an async function, which allows us to use the await keyword in it. Using async makes it easier to write asynchronous code, which improves readability.
The
getUsersFromAPI()
function makes an asynchronous request to the GitHub API to retrieve a list of users, using the current page number to paginate the results.When the data is successfully received, it adds the new users to the existing list and sets the next page number for retrieval.
If there is any exception in the process, it catches the exception and logs it to the console for debugging.
The function always stops the loading indicator, whether it is successful or not, to update the user interface accordingly.
const getUsersFromAPI = async () => {
try {
setIsLoading(true);
const response = await fetch(`https://api.github.com/users?since=${(page - 1) * 30}`);
const data = await response.json();
setUsers((prevUsers) => [...prevUsers, ...data]);
setPage((prevPage) => prevPage + 1);
} catch (error) {
console.error(`Error while fetching users: ${error}`);
} finally {
setIsLoading(false);
}
}
4οΈβ£ Handle Scroll Events
We will create a function called handleScroll
, which checks if the user is near the bottom of the page. If yes, it will invoke the getUsersFromAPI()
function to fetch more users.
It also ensures that, as a request is in progress, no new request is sent (isLoading prevents this).
const handleScroll = () => {
if (isLoading) return;
if (window.innerHeight + window.scrollY >= document.body.offsetHeight - 10) {
getUsersFromAPI();
}
}
5οΈβ£ Setup Lifecycle with useEffect
The useEffect
hook runs once when the component is mounted.
- It makes a call to the
getUsersFromAPI()
method to load the first list of users. - Adds a scroll event listener.
- Cleans the listener when the component is removed.
useEffect(() => {
getUsersFromAPI();
window.addEventListener('scroll', handleScroll);
return () => {
window.removeEventListener('scroll', handleScroll);
}
}, []);
6οΈβ£ Render Users in Card Format
We iterate over the users array using the map()
function and display each user in a stylish card layout. Each card displays the user's avatar, username, and account type (i.e., User or Organization), and a link to their GitHub profile is displayed when the card is hovered over.
{users.map((user) => (
<div className="card" key={user.id}>
<div className="image-container">
<img src={user.avatar_url} alt={user.login} />
</div>
<div className="content-container">
<div className="contentBx">
<h3>
{user.login}<br />
<span>{user.type}</span>
</h3>
</div>
<div className="sci">
<a href={user.html_url} target='blank' rel='noopener noreferrer'>π View Profile</a>
</div>
</div>
</div>
))}
Complete code snippet
import React, { useEffect, useState } from 'react'
import './InfiniteScroll.css'
const InfiniteScroll = () => {
const [users, setUsers] = useState([]);
const [page, setPage] = useState(1);
const [isLoading, setIsLoading] = useState(false);
// function to fecth users from Github API
const getUsersFromAPI = async () => {
try {
setIsLoading(true);
const response = await fetch(`https://api.github.com/users?since=${(page - 1) * 30}`);
const data = await response.json();
setUsers((prevUsers) => [...prevUsers, ...data]);
setPage((prevPage) => prevPage + 1);
} catch (error) {
console.error(`Error while fetching users: ${error}`);
} finally {
setIsLoading(false);
}
}
// function to check scroll position and load more if needed
const handleScroll = () => {
if (isLoading) return;
if (window.innerHeight + window.scrollY >= document.body.offsetHeight - 10) {
getUsersFromAPI();
}
}
// Initial load and attach scroll listener
useEffect(() => {
getUsersFromAPI();
window.addEventListener('scroll', handleScroll);
return () => {
window.removeEventListener('scroll', handleScroll);
}
}, []);
return (
<>
<h1 className='page-title'>Github Users</h1>
<section>
<div className="container">
{users.map((user) => (
<div className="card" key={user.id}>
<div className="image-container">
<img src={user.avatar_url} alt={user.login} />
</div>
<div className="content-container">
<div className="content">
<h3>
{user.login}<br />
<span>{user.type}</span>
</h3>
</div>
<div className="social">
<a href={user.html_url} target='blank' rel='noopener noreferrer'>View Profile</a>
</div>
</div>
</div>
))}
</div>
</section>
</>
)
}
export default InfiniteScroll
7οΈβ£ Styling with CSS π
π¨
CSS is essential for creating engaging user interfaces and enhancing the look and feel of your components.
For styling the InfiniteScroll component, simply copy the provided CSS code and paste it into your InfiniteScroll.css file.
Feel free to customize and experiment with the styles to match your app's design preferences. ποΈ
β‘οΈ In this tutorial, I am using nested CSS, a modern and in-demand trend that improves code readability and maintains cleaner structures, especially when working on component-based designs.
However, if you prefer, you can always stick to the traditional flat CSS syntax, which works just as well. The choice depends on your project needs and personal preference. β¨
.page-title {
text-align: center;
margin: 20px;
}
.container {
position: relative;
display: flex;
justify-content: center;
align-items: center;
flex-wrap: wrap;
gap: 20px;
padding: 20px;
.card {
position: relative;
display: flex;
justify-content: center;
align-items: center;
width: 250px;
height: 200px;
overflow: hidden;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
border-radius: 15px;
&:hover .content-container {
bottom: 0;
transition-delay: 0s;
cursor: pointer;
}
&:hover .content-container .content h3 {
opacity: 1;
transform: translateY(0px);
}
&:hover .content-container .social {
transform: translateY(0px);
opacity: 1;
border: 1px solid #867cc1;
padding: 5px;
border-radius: 10px;
}
.image-container {
position: relative;
height: 100%;
width: 100%;
img {
height: 100%;
width: 100%;
object-fit: cover;
}
}
.content-container {
position: absolute;
bottom: -135px;
width: 100%;
height: 125px;
display: flex;
justify-content: center;
align-items: center;
z-index: 10;
flex-direction: column;
backdrop-filter: blur(15px);
box-shadow: 0 -10px 10px rgba(0, 0, 0, 0.1);
border: 1px solid rgba(255, 255, 255, 0.2);
transition: bottom 0.5s;
transition-delay: 0.3s;
.content {
h3 {
color: #fff;
text-transform: capitalize;
letter-spacing: 2px;
font-weight: bold;
font-size: 18px;
text-align: center;
margin: 10px 0 5px;
line-height: 1.1em;
transition: 0.5s;
opacity: 0;
transform: translateY(-20px);
transition-delay: 0.5s;
}
span {
font-size: 12px;
font-weight: 300;
text-transform: initial;
}
}
.social {
position: relative;
bottom: 0x;
display: flex;
transform: translateY(40px);
opacity: 0;
transition-delay: 0.3s;
a {
color: #d3d3d3;
font-size: 15px;
text-decoration: none;
}
}
}
}
}
π‘ Key Learnings
β
useState
β To manage data, page number, and loading state
β
useEffect
β To handle side-effects and lifecycle events like API calls and scroll listeners
β
fetch API
β To make HTTP requests to the GitHub API
β
map()
β To dynamically render user cards
β
Scroll Event β To detect when the user reaches to the bottom of the page
π Wrapping Up
In this tutorial, we explored how to build a smooth infinite scroll feature in React JS, pulling real-time data from the GitHub API and presenting it in an elegant card layout. πβ¨
The focus was on keeping the code clean and beginner-friendly, sticking to the core React hooks like useState
and useEffect
without diving into complex patterns.
By following along, you now have a solid understanding of how state management, side effects, and event listeners can work together to create engaging, dynamic user experiences. π‘
If you made it this far, Iβd love to hear your feedback or if there's anything you think I could have explained better.
Letβs keep learning together! π
Happy Coding! π¨βπ»π©βπ»π
Top comments (0)