How to handle loading spinners in React
Displaying loading spinners provides visual feedback during async operations, improving perceived performance in React applications. As the creator of CoreUI with over 11 years of React development experience since 2014, I’ve implemented loading states in countless data-fetching scenarios. The most effective solution is to use state to track loading status and conditionally render spinner components. This approach provides clear feedback and improves user experience during data loading.
Use loading state to conditionally render spinners in React.
const [data, setData] = useState(null)
const [loading, setLoading] = useState(false)
const [error, setError] = useState(null)
const fetchData = async () => {
setLoading(true)
setError(null)
try {
const response = await fetch('/api/data')
if (!response.ok) throw new Error('Failed to fetch')
const result = await response.json()
setData(result)
} catch (err) {
setError(err.message)
} finally {
setLoading(false)
}
}
useEffect(() => {
fetchData()
}, [])
if (loading) {
return (
<div className='spinner-container'>
<div className='spinner'></div>
<p>Loading...</p>
</div>
)
}
if (error) {
return <div className='error'>Error: {error}</div>
}
if (!data) {
return <div>No data available</div>
}
return (
<div>
<h1>{data.title}</h1>
<p>{data.content}</p>
</div>
)
.spinner {
border: 4px solid #f3f3f3;
border-top: 4px solid #3498db;
border-radius: 50%;
width: 40px;
height: 40px;
animation: spin 1s linear infinite;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.spinner-container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
min-height: 200px;
}
The loading state toggles between true and false around async operations. The finally block ensures loading is set to false regardless of success or failure. Early returns render different UI based on loading, error, or empty states. The spinner uses pure CSS animation for smooth performance without JavaScript.
Best Practice Note
This is the same loading pattern we use in CoreUI React components for consistent user feedback. For multiple concurrent requests, use separate loading states or a counter to track active requests. Consider using loading skeletons instead of spinners for better perceived performance, showing the layout structure while content loads.



