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
function MyComponent() { const [data, setData] = React.useState(null); const [loading, setLoading] = React.useState(true); useEffect(() => { fetchData().then(response => { setData(response); setLoading(false); }); }, []); if (loading) { return <div>Loading...</div>; } return <div>{data}</div>; }
In this example:
loading
starts astrue
and switches tofalse
once 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
const LazyComponent = React.lazy(() => import('./MyLazyComponent')); function App() { return ( <React.Suspense fallback={<div>Loading component...</div>}> <LazyComponent /> </React.Suspense> ); }
In this example:
- The
LazyComponent
is 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
useEffect
or 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
Suspense
fallback 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.