In modern web applications, user experience is key to retaining visitors and making a lasting impression.
When content loads with a delay, a loading skeleton can significantly improve the user experience by filling the empty space with a visual placeholder, making it seem like content is loading progressively.
In this blog, we’ll explore how to set up React Loading Skeleton, understand its commonly used props, and apply it to a real-world example.
1. Setting Up the Project with Vite
We’ll start by setting up a new React project using Vite, a fast, lightweight bundler ideal for React development. Vite speeds up development and is perfect for creating applications that require minimal setup.
Step 1: Open your terminal and run the following commands:
# Initialize a new React project with Vite
npm create vite@latest react-skeleton-demo --template react
cd react-skeleton-demo
npm install
Step 2: Install the react-loading-skeleton package, which provides pre-made skeletons with various customization options.
npm install react-loading-skeleton
With the setup complete, let’s dive into the purpose of skeleton loaders and their functionality in React applications.
2. Understanding Skeleton Loaders
Skeleton loaders act as placeholders to enhance perceived load times. Instead of showing empty spaces or spinner animations, skeleton loaders display elements shaped like the final content—creating a sense of progress while the data loads.
Here’s a simple example of a skeleton loader in React:
// SkeletonExample.jsx import React from "react"; import Skeleton from "react-loading-skeleton"; import "react-loading-skeleton/dist/skeleton.css"; const SkeletonExample = () => { return ( <div> <p>Basic Skeleton Example:</p> <Skeleton height={30} width={200} /> </div> ); }; export default SkeletonExample;
This example renders a skeleton bar with custom dimensions, giving users a sense of where the actual content will appear.
3. Commonly Used Props for Skeleton Loaders
The react-loading-skeleton library offers a variety of props that let you tailor the loader to your app’s design. Here are some of the most commonly used ones:
width & height: width
and height
sets the dimensions of the skeleton loader to match the eventual content size.
//Render a skeleton loader with a height of 100 pixels and a width of 200 pixels. <Skeleton height={100} width={200} /> //width: Specifies the width of the skeleton loader. //You can provide a value in pixels or percentages. //Example 1: Width of 200 pixels <Skeleton width={200} /> //Example 2: Width of 50% of the parent container <div style={{ width: "500px" }}> <Skeleton width={"50%"} /> </div> //height: Specifies the height of the skeleton loader. //You can provide a value in pixels or percentages. //Example 1: Height of 30 pixels <Skeleton height={30} /> //Example 2: Height of 20% of the parent container <div style={{ height: "200px", border: "1px solid black" }}> <Skeleton height={"20%"} /> </div>
Circle: circle
renders a circular skeleton, ideal for placeholders like profile images.
//circle: When set to true, the skeleton loader will be //displayed as a circle instead of a rectangle. <Skeleton circle width={100} height={100} />
Count: count
displays multiple skeleton lines, useful for text blocks.
//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} /> //Skeleton loader with count of 2.5 <Skeleton count={2.5} />
Duration: duration
adjusts the animation speed, making it pulse faster or slower depending on user preference.
//duration: Specifies the duration of the animation for the //skeleton loader. This prop accepts a value in seconds. //Animation duration of 2.5 seconds <Skeleton duration={2.5} />
Color: color
specifies the color of the skeleton loader. You can provide a
color value in hexadecimal, RGB, RGBA, or CSS color name format.
<Skeleton baseColor="#CB9DF0" />
Style: style
applies additional custom styling to the skeleton, such as border radius or color adjustments.
<Skeleton style={{ borderRadius: "8px", backgroundColor: "#eee" }} />
Inline: By default, a line breaks is inserted after each skeleton so that each skeleton gets its own line. When inline is true, no line breaks are inserted.
<Skeleton circle inline={true} width={100} height={100} /> <Skeleton circle inline={true} width={100} height={100} />
direction: The direction of the animation, either left-to-right or right-to-left.
<Skeleton direction="ltr" baseColor="#CB9DF0" /> <Skeleton direction="rtl" baseColor="#CB9DF0" />
enableAnimation: Whether the animation should play. The skeleton will be a solid color when this is false. You could use this prop to stop the animation if an error occurs.
<Skeleton enableAnimation={false} baseColor="#CB9DF0" />
Using these props, you can customize the skeleton loaders to suit your design, creating a consistent loading experience throughout the app.
Themed Skeletons with Custom Colors
While the default skeleton loaders are highly functional, there are cases where you want them to match the theme of your application. This is where the SkeletonTheme
component from the react-loading-skeleton library comes into play.
Using the SkeletonTheme
, you can define custom colors for the skeleton loader’s base and highlight states. Here’s an example:
Code Example: ThemedSkeleton Component
ThemedSkeleton.jsx
import Skeleton, { SkeletonTheme } from "react-loading-skeleton"; import "react-loading-skeleton/dist/skeleton.css"; const ThemedSkeleton = () => { return ( <> <SkeletonTheme baseColor="#CB9DF0" highlightColor="#F0C1E1"> <Skeleton count={2} /> </SkeletonTheme> </> ); }; export default ThemedSkeleton;
Explanation of the Code
SkeletonTheme: Wraps the skeleton components and applies consistent theming.
- baseColor: The background color of the skeleton (here, a purple shade: #CB9DF0).
- highlightColor: The color that appears during the shimmer animation (here, a pink shade: #F0C1E1).
Skeleton: count={2}, renders two skeleton lines consecutively.
Styling: The skeleton.css file is imported to include default styling for the loaders.
Output:
Real-world Example: Post List Component with Skeleton Loaders
In this example, we’ll create a Post List component that fetches posts from an API and displays skeletons as placeholders while data is being retrieved.
We’ll structure this with two components:
- PostList: The parent component, responsible for fetching a list of posts and rendering each post using the PostCard component.
- PostCard: The child component, which displays skeleton loaders for the image and text content of each post while data is loading.
Now, let’s see how to integrate skeleton loaders into a Post List component that fetches data from an API using axios.
Here we are using Axios for API Integration, for installing use the following command:
npm install axios
As for API, we will be using the JSONPlaceholder API which 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.
Now lets See how the code is going to look like
Step 1: Create the PostList component that fetches posts from a placeholder API.
PostList.jsx
import React, { useState, useEffect } from "react"; import axios from "axios"; import PostCard from './PostCard'; const PostList = () => { const [posts, setPosts] = useState([]); const [loading, setLoading] = useState(true); useEffect(() => { axios .get("https://jsonplaceholder.typicode.com/posts") .then((response) => { console.log('first response', response.data); setPosts(response.data); setLoading(false); }) .catch((error) => console.error(error)); }, []); return ( <div className="post-container"> <div className="post-list"> {loading ? ( <div>Loading...</div> ) : ( posts.map((post) => <PostCard key={post.id} post={post} />) )} </div> </div> ); }; export default PostList;
In this code, we make a GET request to fetch posts. While waiting for the data, we display skeleton placeholders. When the posts are fetched, the skeletons are replaced by actual content in the PostCard component.
Step 2: Create the PostCard component, which displays each post’s content and image with skeleton placeholders.
PostCard.jsx
import React, { useState, useEffect } from "react"; import axios from "axios"; import Skeleton from "react-loading-skeleton"; const PostCard = ({ post }) => { const [imageUrl, setImageUrl] = useState(""); const [loading, setLoading] = useState(true); useEffect(() => { fetchImage(); }, []); const fetchImage = () => { setLoading(true); axios .get("https://picsum.photos/300/200") .then((response) => { console.log("first response", response.request.responseURL); setImageUrl(response.request.responseURL); setLoading(false); }) .catch((error) => console.error(error)); }; return ( <div className="post-card"> <div className="post-image"> {loading ? ( <Skeleton width={"300px"} height={"200px"} /> ) : ( <img src={imageUrl} alt="Post" loading="lazy" /> )} </div> <div className="post-content"> {loading ? ( <> <Skeleton height={20} width={200} style={{ marginBottom: "10px", marginTop: "10px" }} /> <Skeleton height={10} count={3} /> </> ) : ( <> <h3>{post.title}</h3> <p>{post.body}</p> </> )} </div> </div> ); }; export default PostCard;
In the PostCard component, a separate API call is used to fetch a random image. The skeleton loader displays while the image and text are loading, providing a seamless experience.
App.css
.heading { text-align: center; font-size: 2em; color: #333; margin-bottom: 20px; } .post-container { margin: 20px; } .post-list { display: flex; gap: 20px; width: 1000px; margin: 0 auto; flex-wrap: wrap; } .post-card { width: 300px; background-color: #fff; border: 1px solid #e0e0e0; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); } .post-image { width: 300px; height: auto; } .post-image img { max-width: 100%; max-height: 100%; object-fit: cover; } .post-content { margin-bottom: 15px; padding: 15px; } .post-content h3 { text-overflow: ellipsis; overflow: hidden; display: -webkit-box !important; -webkit-line-clamp: 2; -webkit-box-orient: vertical; white-space: normal; text-transform: capitalize; height: 60px; margin-top: 0; margin-bottom: 0; font-size: 18px; font-weight: 600; } .post-content p { text-overflow: ellipsis; overflow: hidden; display: -webkit-box !important; -webkit-line-clamp: 4; -webkit-box-orient: vertical; white-space: normal; text-transform: capitalize; margin-top: 0; margin-bottom: 0; font-size: 14px; font-weight: 400; }
App.jsx
import "./App.css"; import PostCard from "./components/PostCard"; import PostList from "./components/PostList"; function App() { return ( <> <PostList/> </> ); } export default App;
Output:
Conclusion
Skeleton loaders are a powerful tool for enhancing the user experience by reducing perceived load times. With react-loading-skeleton, creating and customizing these loaders is easy, allowing you to build a visually engaging interface that feels faster and keeps users engaged.
Experiment with different props and try integrating skeletons into various parts of your app to create a consistent, professional experience.
Add comment