In today's web development landscape, optimizing user experience is paramount. One common challenge developers face is dealing with loading times, especially when fetching data from external sources. To address this issue, skeleton loaders have emerged as a popular solution. In this blog post, we'll delve into what skeleton loaders are, how they enhance user experience, and how to implement them in a React application using the react-loading-skeleton library.
We'll also explore a real-world example using a Post List component that fetches data using Axios.
Let's dive into the understanding of the Skeleton loader💀!
Setting Up the Project
Before we dive into the implementation, make sure you have a React project set up. If not, you can quickly create one using the Create React App or any other React boilerplate. Once your project is set up, install the react-loading-skeleton library by running the following command in your terminal:
npm install react-loading-skeleton
Understanding Skeleton Loaders:
Skeleton loaders are UI placeholders that mimic the layout of content while data is being fetched or processed. They provide users with a visual indication that something is happening in the background, reducing perceived loading times and enhancing user engagement. Skeleton loaders typically consist of simple, grayscale shapes that resemble the structure of the actual content being loaded, such as text blocks, images, and buttons.
Basic Configuration with react-loading-skeleton:
Once Installation is complete then import the Skeleton component into your React components where you want to use skeleton loaders:
import Skeleton from "react-loading-skeleton";
Use the Skeleton component to create placeholders for your content:
<Skeleton height={100} width={200} />
This will render a skeleton loader with a height of 100 pixels and a width of 200 pixels.
Let's explore some of the commonly used props:
1.width
: Specifies the width of the skeleton loader. You can provide a value in pixels or percentages.
<Skeleton width={200} /> // Width of 200 pixels
<Skeleton width={"50%"} /> // Width of 50% of the parent container
2.height
: Specifies the height of the skeleton loader. You can provide a value in pixels.
<Skeleton height={20} /> // Height of 20 pixels
3.circle
: When set to true, the skeleton loader will be displayed as a circle instead of a rectangle.
<Skeleton circle width={50} height={50} /> // Circle with a diameter of 50 pixels
4.count
: Specifies the number of skeleton loaders to render. Useful when you want to create multiple placeholders for a list of items.
<Skeleton height={20} count={3} /> // Three skeleton loaders for text lines
5.duration
: Specifies the duration of the animation for the skeleton loader. This prop accepts a value in seconds.
<Skeleton duration={1.5} /> // Animation duration of 1.5 seconds
6.color
: Specifies the color of the skeleton loader. You can provide a color value in hexadecimal, RGB, RGBA, or CSS color name format.
<Skeleton color="#f0f0f0" /> // Light gray color
7.style
: Allows you to apply custom styles to the skeleton loader using a JavaScript object.
<Skeleton style={{ borderRadius: "50%", width: 100, height: 100 }} /> // Circular skeleton loader with a width and height of 100 pixels
These are some of the commonly used props for customizing skeleton loaders in react-loading-skeleton. By leveraging these props, you can create skeleton loaders that match the design and layout of your application.
Real-world Example: Post List Component with Skeleton Loaders
Now, let's see how to integrate skeleton loaders into a Post List component that fetches data from an API using axios.
Here I am using Axios for API Integration, for installing use the following command:
npm install axios
as for API, we will be using the JSONPlaceholder API is a free online REST API that provides placeholder data in the form of JSON responses. It's commonly used for testing and prototyping applications without the need for a real backend. The API provides various endpoints for different resources such as posts, users, comments, etc.
Here's a shorter breakdown of the structure of the example:
PostList Page (PostList.tsx):
- Fetches a list of posts from the JSONPlaceholder API using Axios.
- Displays skeleton loaders while data is being fetched.
- Renders PostCard components with fetched data or skeleton loaders.
PostCard Component (PostCard.tsx):
- Represents a single post card.
- Displays post data if available, otherwise displays skeleton loaders.
App Component (App.tsx):
- Renders the PostList component.
CSS Styles (App.css):
- Contains styles for components and skeleton loaders.
Axios:
- Used to make HTTP requests to the JSONPlaceholder API.
Now lets See how the code is going to look like
- create a file named as PostCard.tsx
import React, { useState, useEffect } from "react";
import axios from "axios";
import Skeleton from "react-loading-skeleton";
interface PostCardProps {
post: {
id: number;
title: string;
body: string;
};
}
const PostCard: React.FC<PostCardProps> = ({ post }) => {
const [imageUrl, setImageUrl] = useState<string>("");
const [loading, setLoading] = useState<boolean>(true);
useEffect(() => {
fetchImage();
}, []);
const fetchImage = () => {
setLoading(true);
axios
.get("https://picsum.photos/200")
.then((response) => {
setImageUrl(response.request.responseURL);
setLoading(false);
})
.catch((error) => console.error(error));
};
return (
<div className="post-card">
<div className="post-image">
{loading ? (
<Skeleton height={"50vh"} width={"30vw"} className="skeleton" />
) : (
<img src={imageUrl} alt="Post" />
)}
</div>
<div className="post-content">
{loading ? (
<React.Fragment>
<Skeleton
height={20}
width={200}
style={{ marginBottom: "10px", marginTop: "10px" }}
className="skeleton"
/>
<Skeleton height={60} count={3} className="skeleton" />
</React.Fragment>
) : (
<React.Fragment>
<center>
<h3>{post.title}</h3>
<p>{post.body}</p>
</center>
</React.Fragment>
)}
</div>
</div>
);
};
export default PostCard;
It defines a React component responsible for rendering a single postcard. It receives a post object as a prop containing the post's data (id, title, body). If the post data is available, it displays the post's title and body. If the post data is not available (while data is being fetched), it displays skeleton loaders for the post's title and body to provide a loading indicator.
2.After this, Let's work on the list view where our cards will be placed, for that in your views or screen folder create PostList.tsx:
import React, { useState, useEffect } from "react";
import axios from "axios";
import PostCard from "../components/PostCard";
const PostList: React.FC = () => {
const [posts, setPosts] = useState<any[]>([]);
const [loading, setLoading] = useState<boolean>(true);
useEffect(() => {
axios
.get("https://jsonplaceholder.typicode.com/posts")
.then((response) => {
setPosts(response.data);
setLoading(false);
})
.catch((error) => console.error(error));
}, []);
return (
<div className="post-list-container">
<div className="post-list">
{loading ? (
<div>Loading...</div>
) : (
posts.map((post) => <PostCard key={post.id} post={post} />)
)}
</div>
</div>
);
};
export default PostList;
Our PostList.tsx
is a React component responsible for fetching a list of posts from an API and rendering them. It uses Axios to make HTTP GET requests to the JSONPlaceholder API to fetch the posts. While the data is being fetched, it displays skeleton loaders to provide visual feedback to the user. Once the data is fetched successfully, it renders the PostCard components with the fetched data. If there is an error while fetching the data, it logs the error to the console.
3.Now finally lets style our list and post cards, here is example of App.css
:
.heading {
text-align: center;
font-size: 2em;
color: #333;
margin-bottom: 20px;
}
.post-list-container {
justify-content: center;
align-items: center;
margin: 50px;
height: 100vh;
}
.post-list {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 20px;
}
.post-card {
padding: 20px;
background-color: #fff;
border: 1px solid #e0e0e0;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
justify-content: center;
}
.post-image {
height: 150px;
overflow: hidden;
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
}
.post-image img {
max-width: 100%;
max-height: 100%;
object-fit: cover;
border-radius: 8px;
}
.post-content {
justify-content: center;
margin-bottom: 10px;
}
.skeleton {
background-color: #d3d3d3;
border-radius: 8px;
display: flex;
}
.skeleton > div {
margin-bottom: 10px;
}
The App.css file contains styles for the application's layout, postcards, and skeleton loaders. It defines:
- Styles for the container of the post list, centering it both horizontally and vertically with a margin and setting its height to fill the viewport.
- Styles for the grid layout of post cards, with columns automatically fitting the available space and a gap between items.
- Styles for individual post cards, including padding, background color, border, border radius, and box shadow for a card-like appearance.
- Styles for skeleton loaders, specifying their background color and border radius to match the design of the post cards.
Here is how the Skeleton loader looks while loading:
and here is how the cards look after loading:
Hope you will find this blog useful ✌🏽🎉
Conclusion:
we've unlocked the potential of skeleton loaders in React, transforming loading times into opportunities for user engagement and satisfaction. By implementing skeleton loaders with Axios and the JSONPlaceholder API, we've learned how to provide users with seamless loading experiences while fetching data asynchronously. Now equipped with this knowledge, you're ready to elevate the user experience of your React applications with confidence. Happy coding and may your apps shine brightly! ✨🚀
Happy Coding✌🏽✨!
For further exploration, feel free to check out the full code implementation on our GitHub repository: react_skeleton_loader. Happy coding and may your apps shine brightly! ✨🎉"
Top comments (0)