react-loader-spinner is a collection of animated loading spinner components for React applications. It provides a variety of spinner styles to indicate loading states, making it easy to provide visual feedback during async operations. This guide walks through setting up and creating loading spinners using react-loader-spinner with React, from installation to a working implementation. This is part 50 of a series on using react-loader-spinner with React.
Prerequisites
Before you begin, make sure you have:
- Node.js version 14.0 or higher installed
- npm, yarn, or pnpm package manager
- A React project (version 16.8 or higher) or create-react-app setup
- Basic knowledge of React hooks (useState, useEffect)
- Familiarity with JavaScript/TypeScript
- Understanding of async operations
Installation
Install react-loader-spinner using your preferred package manager:
npm install react-loader-spinner
Or with yarn:
yarn add react-loader-spinner
Or with pnpm:
pnpm add react-loader-spinner
After installation, your package.json should include:
{
"dependencies": {
"react-loader-spinner": "^5.0.0",
"react": "^18.0.0",
"react-dom": "^18.0.0"
}
}
Project Setup
react-loader-spinner requires no additional setup. Import the spinner components and you're ready to use them.
First Example / Basic Usage
Let's create a simple loading spinner. Create a new file src/LoaderExample.jsx:
// src/LoaderExample.jsx
import React, { useState } from 'react';
import { Oval } from 'react-loader-spinner';
function LoaderExample() {
const [isLoading, setIsLoading] = useState(false);
const handleLoad = async () => {
setIsLoading(true);
// Simulate API call
await new Promise(resolve => setTimeout(resolve, 2000));
setIsLoading(false);
};
return (
<div style={{ padding: '20px', textAlign: 'center' }}>
<h2>Loading Spinner Example</h2>
<button
onClick={handleLoad}
disabled={isLoading}
style={{
padding: '10px 20px',
backgroundColor: '#007bff',
color: 'white',
border: 'none',
borderRadius: '4px',
cursor: isLoading ? 'not-allowed' : 'pointer',
marginBottom: '20px'
}}
>
{isLoading ? 'Loading...' : 'Start Loading'}
</button>
{isLoading && (
<div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center' }}>
<Oval
height={50}
width={50}
color="#007bff"
visible={true}
ariaLabel="loading"
secondaryColor="#cccccc"
strokeWidth={2}
strokeWidthSecondary={2}
/>
</div>
)}
</div>
);
}
export default LoaderExample;
Update your App.jsx:
// src/App.jsx
import React from 'react';
import LoaderExample from './LoaderExample';
import './App.css';
function App() {
return (
<div className="App">
<LoaderExample />
</div>
);
}
export default App;
This creates a loading spinner that appears when the button is clicked and disappears after the async operation completes.
Understanding the Basics
react-loader-spinner provides many spinner types:
- Oval: Oval-shaped spinner
- ThreeDots: Three dots animation
- TailSpin: Tail spin animation
- Rings: Multiple rings
- Puff: Puff animation
- Grid: Grid pattern
- Audio: Audio waveform
- BallTriangle: Ball triangle animation
- Bars: Bars animation
- Circles: Multiple circles
- And many more...
Key concepts:
- Spinner Components: Each spinner type is a separate component
-
Visible Prop: Control visibility with the
visibleprop - Customization: Size, color, and animation speed can be customized
-
Accessibility: Use
ariaLabelfor screen readers - Conditional Rendering: Show/hide spinners based on loading state
Here's an example with different spinner types:
// src/MultipleSpinnersExample.jsx
import React, { useState } from 'react';
import { Oval, ThreeDots, TailSpin, Rings, Puff } from 'react-loader-spinner';
function MultipleSpinnersExample() {
const [selectedSpinner, setSelectedSpinner] = useState('Oval');
const spinners = {
Oval: <Oval height={50} width={50} color="#007bff" visible={true} ariaLabel="loading" />,
ThreeDots: <ThreeDots height={50} width={50} color="#007bff" visible={true} ariaLabel="loading" />,
TailSpin: <TailSpin height={50} width={50} color="#007bff" visible={true} ariaLabel="loading" />,
Rings: <Rings height={50} width={50} color="#007bff" visible={true} ariaLabel="loading" />,
Puff: <Puff height={50} width={50} color="#007bff" visible={true} ariaLabel="loading" />
};
return (
<div style={{ padding: '20px', textAlign: 'center' }}>
<h2>Multiple Spinner Types</h2>
<div style={{ marginBottom: '20px' }}>
<label>Select Spinner: </label>
<select
value={selectedSpinner}
onChange={(e) => setSelectedSpinner(e.target.value)}
style={{ padding: '5px', marginLeft: '10px' }}
>
{Object.keys(spinners).map(type => (
<option key={type} value={type}>{type}</option>
))}
</select>
</div>
<div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', minHeight: '200px' }}>
{spinners[selectedSpinner]}
</div>
</div>
);
}
export default MultipleSpinnersExample;
Practical Example / Building Something Real
Let's build a comprehensive loading system with different spinner types and use cases:
// src/LoadingSystem.jsx
import React, { useState } from 'react';
import { Oval, ThreeDots, TailSpin, Rings } from 'react-loader-spinner';
function LoadingSystem() {
const [loadingStates, setLoadingStates] = useState({
button: false,
page: false,
inline: false,
overlay: false
});
const simulateApiCall = async (type) => {
setLoadingStates(prev => ({ ...prev, [type]: true }));
await new Promise(resolve => setTimeout(resolve, 2000));
setLoadingStates(prev => ({ ...prev, [type]: false }));
};
return (
<div style={{ padding: '20px' }}>
<h1>Loading System Examples</h1>
{/* Button Loading */}
<section style={{ marginBottom: '40px', padding: '20px', border: '1px solid #ddd', borderRadius: '8px' }}>
<h2>Button Loading</h2>
<button
onClick={() => simulateApiCall('button')}
disabled={loadingStates.button}
style={{
padding: '10px 20px',
backgroundColor: '#007bff',
color: 'white',
border: 'none',
borderRadius: '4px',
cursor: loadingStates.button ? 'not-allowed' : 'pointer',
display: 'flex',
alignItems: 'center',
gap: '10px'
}}
>
{loadingStates.button && (
<Oval
height={20}
width={20}
color="white"
visible={true}
ariaLabel="loading"
/>
)}
{loadingStates.button ? 'Loading...' : 'Submit'}
</button>
</section>
{/* Inline Loading */}
<section style={{ marginBottom: '40px', padding: '20px', border: '1px solid #ddd', borderRadius: '8px' }}>
<h2>Inline Loading</h2>
<button
onClick={() => simulateApiCall('inline')}
style={{
padding: '10px 20px',
backgroundColor: '#28a745',
color: 'white',
border: 'none',
borderRadius: '4px',
cursor: 'pointer',
marginBottom: '20px'
}}
>
Load Data
</button>
{loadingStates.inline ? (
<div style={{ display: 'flex', justifyContent: 'center', padding: '20px' }}>
<TailSpin
height={40}
width={40}
color="#007bff"
visible={true}
ariaLabel="loading"
/>
</div>
) : (
<div style={{ padding: '20px', backgroundColor: '#f8f9fa', borderRadius: '4px' }}>
<p>Data loaded successfully!</p>
</div>
)}
</section>
{/* Page Loading */}
<section style={{ marginBottom: '40px', padding: '20px', border: '1px solid #ddd', borderRadius: '8px' }}>
<h2>Page Loading</h2>
<button
onClick={() => simulateApiCall('page')}
style={{
padding: '10px 20px',
backgroundColor: '#ffc107',
color: 'black',
border: 'none',
borderRadius: '4px',
cursor: 'pointer'
}}
>
Load Page Content
</button>
{loadingStates.page && (
<div style={{
position: 'fixed',
top: 0,
left: 0,
right: 0,
bottom: 0,
backgroundColor: 'rgba(255, 255, 255, 0.9)',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
zIndex: 1000
}}>
<div style={{ textAlign: 'center' }}>
<Rings
height={80}
width={80}
color="#007bff"
visible={true}
ariaLabel="loading"
/>
<p style={{ marginTop: '20px', fontSize: '18px' }}>Loading page content...</p>
</div>
</div>
)}
</section>
{/* Overlay Loading */}
<section style={{ marginBottom: '40px', padding: '20px', border: '1px solid #ddd', borderRadius: '8px', position: 'relative' }}>
<h2>Overlay Loading</h2>
<button
onClick={() => simulateApiCall('overlay')}
style={{
padding: '10px 20px',
backgroundColor: '#dc3545',
color: 'white',
border: 'none',
borderRadius: '4px',
cursor: 'pointer'
}}
>
Process Data
</button>
<div style={{
padding: '20px',
backgroundColor: '#f8f9fa',
borderRadius: '4px',
marginTop: '20px',
minHeight: '200px'
}}>
<p>Content area that can be overlaid with a loading spinner.</p>
</div>
{loadingStates.overlay && (
<div style={{
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
backgroundColor: 'rgba(0, 0, 0, 0.5)',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
borderRadius: '8px'
}}>
<ThreeDots
height={50}
width={50}
color="#ffffff"
visible={true}
ariaLabel="loading"
/>
</div>
)}
</section>
</div>
);
}
export default LoadingSystem;
Now create a reusable loading hook:
// src/hooks/useLoading.js
import { useState, useCallback } from 'react';
export const useLoading = (initialState = false) => {
const [isLoading, setIsLoading] = useState(initialState);
const startLoading = useCallback(() => {
setIsLoading(true);
}, []);
const stopLoading = useCallback(() => {
setIsLoading(false);
}, []);
const withLoading = useCallback(async (asyncFn) => {
setIsLoading(true);
try {
const result = await asyncFn();
return result;
} finally {
setIsLoading(false);
}
}, []);
return {
isLoading,
startLoading,
stopLoading,
withLoading
};
};
Create a component using the hook:
// src/LoadingWithHook.jsx
import React from 'react';
import { useLoading } from './hooks/useLoading';
import { Oval } from 'react-loader-spinner';
function LoadingWithHook() {
const { isLoading, withLoading } = useLoading();
const fetchData = async () => {
// Simulate API call
await new Promise(resolve => setTimeout(resolve, 2000));
return { data: 'Loaded data' };
};
const handleClick = async () => {
const result = await withLoading(fetchData);
console.log(result);
};
return (
<div style={{ padding: '20px', textAlign: 'center' }}>
<h2>Loading with Custom Hook</h2>
<button
onClick={handleClick}
disabled={isLoading}
style={{
padding: '10px 20px',
backgroundColor: '#007bff',
color: 'white',
border: 'none',
borderRadius: '4px',
cursor: isLoading ? 'not-allowed' : 'pointer',
marginBottom: '20px'
}}
>
{isLoading ? 'Loading...' : 'Fetch Data'}
</button>
{isLoading && (
<div style={{ display: 'flex', justifyContent: 'center' }}>
<Oval
height={50}
width={50}
color="#007bff"
visible={true}
ariaLabel="loading"
/>
</div>
)}
</div>
);
}
export default LoadingWithHook;
Update your App.jsx:
// src/App.jsx
import React from 'react';
import LoadingSystem from './LoadingSystem';
import LoadingWithHook from './LoadingWithHook';
import './App.css';
function App() {
return (
<div className="App">
<LoadingSystem />
<LoadingWithHook />
</div>
);
}
export default App;
This example demonstrates:
- Multiple spinner types
- Button loading states
- Inline loading indicators
- Page-level loading
- Overlay loading
- Custom loading hook
- Async operation handling
Common Issues / Troubleshooting
Spinner not displaying: Make sure the
visibleprop is set totrue. Also check that the component is being rendered (not conditionally hidden).Spinner too small or large: Adjust the
heightandwidthprops to control the spinner size. These values are in pixels.Color not changing: Use the
colorprop to set the spinner color. It accepts any valid CSS color value (hex, rgb, named colors).Spinner not animating: Ensure the
visibleprop istrue. The spinner only animates when visible.Multiple spinners: You can use multiple spinner instances on the same page. Each spinner manages its own visibility independently.
Accessibility: Always provide an
ariaLabelprop for screen readers. This helps users with assistive technologies understand what's loading.
Next Steps
Now that you have an understanding of react-loader-spinner:
- Explore all available spinner types
- Learn about advanced customization options
- Implement loading states in forms and data fetching
- Add loading indicators to async operations
- Create custom loading components
- Learn about other loading libraries (react-spinners, react-loading)
- Check the official repository: https://github.com/mhnpd/react-loader-spinner
- Look for part 51 of this series for more advanced topics
Summary
You've successfully set up react-loader-spinner in your React application and created loading indicators for buttons, inline content, page loading, and overlays. react-loader-spinner provides a variety of spinner types to indicate loading states with minimal configuration.
SEO Keywords
react-loader-spinner
React loading spinner
react-loader-spinner tutorial
React loading indicator
react-loader-spinner installation
React spinner component
react-loader-spinner example
React async loading
react-loader-spinner setup
React loading states
react-loader-spinner customization
React spinner types
react-loader-spinner hooks
React loading library
react-loader-spinner getting started
Top comments (0)