react-spinners is a collection of animated loading spinner components for React applications. It provides a variety of spinner styles with customizable colors, sizes, and speeds, making it easy to add professional loading indicators to your app. This guide walks through setting up and creating loading spinners using react-spinners with React, from installation to a working implementation. This is part 53 of a series on using react-spinners 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-spinners using your preferred package manager:
npm install react-spinners
Or with yarn:
yarn add react-spinners
Or with pnpm:
pnpm add react-spinners
After installation, your package.json should include:
{
"dependencies": {
"react-spinners": "^0.13.0",
"react": "^18.0.0",
"react-dom": "^18.0.0"
}
}
Project Setup
react-spinners 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/SpinnerExample.jsx:
// src/SpinnerExample.jsx
import React, { useState } from 'react';
import { ClipLoader } from 'react-spinners';
function SpinnerExample() {
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' }}>
<ClipLoader color="#007bff" size={50} />
</div>
)}
</div>
);
}
export default SpinnerExample;
Update your App.jsx:
// src/App.jsx
import React from 'react';
import SpinnerExample from './SpinnerExample';
import './App.css';
function App() {
return (
<div className="App">
<SpinnerExample />
</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-spinners provides many spinner types:
- ClipLoader: Clip animation
- BeatLoader: Beat animation
- BounceLoader: Bounce animation
- CircleLoader: Circle animation
- ClimbingBoxLoader: Climbing box animation
- DotLoader: Dot animation
- FadeLoader: Fade animation
- GridLoader: Grid animation
- HashLoader: Hash animation
- MoonLoader: Moon animation
- PacmanLoader: Pacman animation
- PropagateLoader: Propagate animation
- PuffLoader: Puff animation
- PulseLoader: Pulse animation
- RingLoader: Ring animation
- RiseLoader: Rise animation
- RotateLoader: Rotate animation
- ScaleLoader: Scale animation
- SyncLoader: Sync animation
- And many more...
Key concepts:
- Spinner Components: Each spinner type is a separate component
- Color Prop: Customize spinner color
- Size Prop: Control spinner size
-
Loading Prop: Control visibility with
loadingprop - SpeedMultiplier: Adjust animation speed
-
CSS Override: Customize with
cssOverrideprop
Here's an example with different spinner types and customization:
// src/MultipleSpinnersExample.jsx
import React, { useState } from 'react';
import { ClipLoader, BeatLoader, BounceLoader, CircleLoader, HashLoader } from 'react-spinners';
function MultipleSpinnersExample() {
const [selectedSpinner, setSelectedSpinner] = useState('ClipLoader');
const [color, setColor] = useState('#007bff');
const [size, setSize] = useState(50);
const [speed, setSpeed] = useState(1);
const spinners = {
ClipLoader: <ClipLoader color={color} size={size} speedMultiplier={speed} />,
BeatLoader: <BeatLoader color={color} size={size} speedMultiplier={speed} />,
BounceLoader: <BounceLoader color={color} size={size} speedMultiplier={speed} />,
CircleLoader: <CircleLoader color={color} size={size} speedMultiplier={speed} />,
HashLoader: <HashLoader color={color} size={size} speedMultiplier={speed} />
};
return (
<div style={{ padding: '20px', textAlign: 'center' }}>
<h2>Multiple Spinner Types</h2>
<div style={{ marginBottom: '20px', display: 'flex', flexDirection: 'column', gap: '10px', maxWidth: '300px', margin: '0 auto 20px' }}>
<div>
<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>
<label>Color: </label>
<input
type="color"
value={color}
onChange={(e) => setColor(e.target.value)}
style={{ marginLeft: '10px' }}
/>
</div>
<div>
<label>Size: </label>
<input
type="range"
min="20"
max="100"
value={size}
onChange={(e) => setSize(Number(e.target.value))}
style={{ marginLeft: '10px' }}
/>
<span style={{ marginLeft: '10px' }}>{size}px</span>
</div>
<div>
<label>Speed: </label>
<input
type="range"
min="0.5"
max="2"
step="0.1"
value={speed}
onChange={(e) => setSpeed(Number(e.target.value))}
style={{ marginLeft: '10px' }}
/>
<span style={{ marginLeft: '10px' }}>{speed}x</span>
</div>
</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 { ClipLoader, BeatLoader, HashLoader, PulseLoader, ScaleLoader } from 'react-spinners';
function LoadingSystem() {
const [loadingStates, setLoadingStates] = useState({
button: false,
inline: false,
page: 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 && <ClipLoader color="white" size={20} />}
{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' }}>
<HashLoader color="#007bff" size={40} />
</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' }}>
<PulseLoader color="#007bff" size={15} />
<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'
}}>
<ScaleLoader color="#ffffff" />
</div>
)}
</section>
</div>
);
}
export default LoadingSystem;
Now create a reusable loading component with CSS override:
// src/components/LoadingIndicator.jsx
import React from 'react';
import { ClipLoader } from 'react-spinners';
function LoadingIndicator({ size = 50, color = '#007bff', fullScreen = false, cssOverride = {} }) {
const defaultCssOverride = {
display: 'block',
margin: '0 auto',
...cssOverride
};
if (fullScreen) {
return (
<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
}}>
<ClipLoader color={color} size={size} cssOverride={defaultCssOverride} />
</div>
);
}
return (
<div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', padding: '20px' }}>
<ClipLoader color={color} size={size} cssOverride={defaultCssOverride} />
</div>
);
}
export default LoadingIndicator;
Update your App.jsx:
// src/App.jsx
import React from 'react';
import LoadingSystem from './LoadingSystem';
import MultipleSpinnersExample from './MultipleSpinnersExample';
import './App.css';
function App() {
return (
<div className="App">
<LoadingSystem />
<MultipleSpinnersExample />
</div>
);
}
export default App;
This example demonstrates:
- Multiple spinner types
- Button loading states
- Inline loading indicators
- Page-level loading
- Overlay loading
- Customizable colors, sizes, and speeds
- CSS override for advanced styling
Common Issues / Troubleshooting
Spinner not displaying: Make sure the
loadingprop is set totrue(if using it) or the component is conditionally rendered. Also check that you're importing the correct spinner component.Spinner too small or large: Adjust the
sizeprop to control the spinner size. The size is in pixels.Color not changing: Use the
colorprop to set the spinner color. It accepts any valid CSS color value (hex, rgb, named colors).Animation too fast or slow: Use the
speedMultiplierprop to adjust animation speed. Values less than 1 slow down, values greater than 1 speed up.Styling conflicts: Use the
cssOverrideprop to override default styles. This is useful when you need to customize positioning or other CSS properties.Multiple spinners: You can use multiple spinner instances on the same page. Each spinner manages its own state independently.
Next Steps
Now that you have an understanding of react-spinners:
- Explore all available spinner types
- Learn about advanced customization with cssOverride
- Implement loading states in forms and data fetching
- Add loading indicators to async operations
- Create custom loading components
- Learn about other loading libraries (react-loader-spinner, react-spinners-css)
- Check the official repository: https://github.com/davidhu2000/react-spinners
- Look for part 54 of this series for more advanced topics
Summary
You've successfully set up react-spinners in your React application and created loading indicators for buttons, inline content, page loading, and overlays. react-spinners provides a variety of spinner types with extensive customization options for professional loading indicators.
SEO Keywords
react-spinners
React loading spinner
react-spinners tutorial
React spinner component
react-spinners installation
React loading indicator
react-spinners example
React animated spinner
react-spinners setup
React spinner library
react-spinners customization
React spinner types
react-spinners cssOverride
React loading library
react-spinners getting started
Top comments (0)