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.


Speed up your responsive apps and websites with fully-featured, ready-to-use open-source admin panel templates—free to use and built for efficiency.


About the Author

Subscribe to our newsletter
Get early information about new products, product updates and blog posts.
What Does javascript:void(0) Mean?
What Does javascript:void(0) Mean?

How to Use Bootstrap Dropdown in Vue 3 – CoreUI Integration Guide
How to Use Bootstrap Dropdown in Vue 3 – CoreUI Integration Guide

How to convert a string to boolean in JavaScript
How to convert a string to boolean in JavaScript

How to migrate CoreUI React Templates to Vite
How to migrate CoreUI React Templates to Vite

Answers by CoreUI Core Team