Asynchronous content loading is a common pattern in modern React applications. React provides two common ways to handle it: the classic loading state and the more recent Suspense fallback. Both techniques serve the same goal—managing the display of content while waiting for asynchronous data—but they are used in different contexts and have different advantages.
In this article, we’ll dive into when to use each approach, explore code examples, and provide a comparison table to clarify their key differences.
What is a Loading State?
A loading state is a manual way of handling asynchronous operations in a React component. It typically involves managing state variables that track whether content is loading, has loaded, or has failed. This approach is particularly useful when you’re fetching data directly inside the component, for example, using useEffect or event handlers like onClick.
Example: Loading State
1function MyComponent() { 2 const [data, setData] = React.useState(null); 3 const [loading, setLoading] = React.useState(true); 4 5 useEffect(() => { 6 fetchData().then(response => { 7 setData(response); 8 setLoading(false); 9 }); 10 }, []); 11 12 if (loading) { 13 return <div>Loading...</div>; 14 } 15 16 return <div>{data}</div>; 17} 18
In this example:
loadingstarts astrueand switches tofalseonce data is fetched.- Conditional rendering is used to display a loading indicator until the data is available.
What is Suspense Fallback?
Suspense is a declarative way of handling async content that integrates with React’s concurrent rendering capabilities. It allows you to wrap components that might take some time to load and specify a fallback UI to display while waiting.
Suspense is primarily used for lazy-loading components (via React.lazy()) or when working with concurrent features like React Server Components or libraries like Relay or React Query that support data fetching with Suspense.
Example: Suspense Fallback
1const LazyComponent = React.lazy(() => import('./MyLazyComponent')); 2 3function App() { 4 return ( 5 <React.Suspense fallback={<div>Loading component...</div>}> 6 <LazyComponent /> 7 </React.Suspense> 8 ); 9} 10
In this example:
- The
LazyComponentis lazy-loaded usingReact.lazy(). - While the component is being loaded, the UI shows a fallback loader (
Loading component...).
When to Use Each Approach
Choosing between a loading state and Suspense fallback depends on the scenario and how you’re handling asynchronous data or component loading in your app.
Use Loading State When:
- You are managing data fetching manually using hooks like
useEffector event-based handlers likeonClick. - You need custom control over the loading process, such as triggering loaders after user interactions or specific side effects.
- You are dealing with form submissions or other one-off asynchronous processes.
Use Suspense Fallback When:
- You are lazy-loading components using
React.lazy(). - You are working with Concurrent React features or third-party libraries (e.g., React Query, Relay) that integrate with Suspense for handling asynchronous data.
- You want a declarative approach to show fallback content when parts of the UI are not yet available.
Comparison Table: Loading State vs Suspense Fallback
| Aspect | Loading State | Suspense Fallback |
|---|---|---|
| Primary Use Case | Data fetching, form submissions, custom async logic | Lazy-loaded components, concurrent data fetching |
| Trigger | Manually controlled via state (useState) | Automatically handled by React when wrapped components are delayed |
| Granularity | Fine-grained, specific to component logic | Declarative, component-level handling |
| Libraries/Features | Used with fetch, useEffect, or similar | Requires React.lazy() or libraries supporting Suspense |
| Rendering Control | Full control over when to display/loading logic | React handles fallback rendering for lazy content |
| Concurrency | No support for concurrent rendering | Supports concurrent rendering with async boundaries |
| Example | Manual loading UI | Suspense fallback UI |
Summary
- Use a loading state when you need fine-grained control over async operations like API requests, data fetching, or form handling in your component logic. It’s a flexible and manual way to manage loading in React apps.
- Use
Suspensefallback when you're lazy-loading components or using React libraries that support concurrent rendering. It provides a declarative, higher-level approach to handle waiting states while loading components or data.
Both techniques are important in modern React development. Loading states are great for smaller, isolated async tasks, while Suspense fallback shines in apps that leverage React’s built-in concurrent features.
By understanding when and how to use these tools, you can provide a smoother user experience while asynchronous content loads in your React applications.
This should give you a clear understanding of when to use loading states and Suspense fallback in React and how they compare in different contexts.
