React Suspense for Data
Handle async data loading declaratively
⏳ What is Suspense for Data?
Suspense for data fetching lets you declaratively handle loading states while components wait for data. It provides a unified way to show fallback UI during async operations without manual loading states.
<Suspense fallback={<Loading />}>
<UserProfile />
</Suspense>
Key Concepts
Declarative
No manual loading state management
Async Handling
Wait for data before rendering
Fallback UI
Show loading states elegantly
Concurrent
Works with React 18+ features
🔹 Basic Suspense Pattern
Suspense wraps components that fetch data, showing a fallback while waiting. The component "suspends" rendering until data is ready, eliminating the need for manual loading state variables.
import { Suspense } from 'react';
function App() {
return (
<div>
<h1>User Dashboard</h1>
{/* Show loading while data fetches */}
<Suspense fallback={<div>Loading user data...</div>}>
<UserData />
</Suspense>
</div>
);
}
// Component that fetches data
function UserData() {
const user = fetchUser(); // Suspends until ready
return <div>Welcome, {user.name}!</div>;
}
Output:
User Dashboard
Loading user data... → Welcome, John!
🔹 Multiple Suspense Boundaries
Use multiple Suspense components to control loading states independently. Each boundary can have its own fallback, allowing parts of your UI to load progressively without blocking other sections.
import { Suspense } from 'react';
function Dashboard() {
return (
<div>
{/* Profile loads independently */}
<Suspense fallback={<div>Loading profile...</div>}>
<UserProfile />
</Suspense>
{/* Posts load independently */}
<Suspense fallback={<div>Loading posts...</div>}>
<UserPosts />
</Suspense>
{/* Comments load independently */}
<Suspense fallback={<div>Loading comments...</div>}>
<UserComments />
</Suspense>
</div>
);
}
Behavior:
Each section loads independently, showing content as soon as it's ready.
🔹 Nested Suspense
Nest Suspense boundaries for granular loading control:
import { Suspense } from 'react';
function App() {
return (
<Suspense fallback={<div>Loading app...</div>}>
<Layout>
{/* Nested suspense for specific sections */}
<Suspense fallback={<div>Loading sidebar...</div>}>
<Sidebar />
</Suspense>
<Suspense fallback={<div>Loading content...</div>}>
<MainContent />
</Suspense>
</Layout>
</Suspense>
);
}
🔹 Using with React Query
Combine Suspense with data fetching libraries for powerful data management. React Query and similar libraries have built-in Suspense support for seamless integration.
import { Suspense } from 'react';
import { useQuery } from '@tanstack/react-query';
function UserProfile() {
// Enable suspense mode in React Query
const { data } = useQuery({
queryKey: ['user'],
queryFn: fetchUser,
suspense: true
});
return (
<div>
<h2>{data.name}</h2>
<p>{data.email}</p>
</div>
);
}
function App() {
return (
<Suspense fallback={<div>Loading...</div>}>
<UserProfile />
</Suspense>
);
}
🔹 Error Handling with Suspense
Combine Suspense with Error Boundaries for complete async handling:
import { Suspense } from 'react';
import { ErrorBoundary } from 'react-error-boundary';
function App() {
return (
<ErrorBoundary fallback={<div>Error loading data!</div>}>
<Suspense fallback={<div>Loading...</div>}>
<DataComponent />
</Suspense>
</ErrorBoundary>
);
}
🔹 Best Practices
✅ Do:
- Use multiple boundaries for independent sections
- Combine with Error Boundaries
- Show meaningful loading messages
- Use with data fetching libraries
❌ Don't:
- Use for non-async operations
- Forget error handling
- Create too many nested boundaries
- Mix with manual loading states