📅 Published at: March 5, 2022
React Suspense has evolved significantly in React 18, making it easier than ever to handle loading states while keeping apps responsive and performant.
In this guide, we’ll cover:
✅ What’s new in Suspense with React 18
✅ How to use Suspense for data fetching
✅ Integrating Suspense with React Query for seamless loading states
✅ Real-world examples & best practices
What Is Suspense in React?
Suspense is a built-in React component that helps manage loading states by pausing rendering until the data is ready.
Instead of using useEffect + useState to show loading indicators, you can wrap components in , and React will automatically handle when they appear.
🔹 React 18 improved Suspense by making it work with data fetching, server components, and React Query.
1️⃣ Basic Example: Suspense with React.lazy()
for Code Splitting
The simplest use of Suspense is lazy-loading components with React.lazy()
:
import React, { Suspense, lazy } from "react";
const LazyComponent = lazy(() => import("./MyComponent"));
function App() {
return (
<Suspense fallback={<div>Loading...</div>}>
<LazyComponent />
</Suspense>
);
}
export default App;
✅ How it works:
React.lazy()
dynamically loads the component when needed.<Suspense fallback={...}>
shows a loading state while fetching.- Once loading is complete, React automatically replaces the fallback.
2️⃣ Suspense for Data Fetching in React 18
With React 18, Suspense can now handle async data fetching directly—without needing useEffect
.
📌 Example: Fetching Data Without Suspense (Traditional Way)
import { useEffect, useState } from "react";
function UserProfile() {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetch("/api/user")
.then(res => res.json())
.then(data => {
setUser(data);
setLoading(false);
});
}, []);
if (loading) return <div>Loading user...</div>;
return <h1>Welcome, {user.name}!</h1>;
}
🔴 Problems:
- Imperative loading logic (manual
loading
state). - Extra renders before showing data.
- Difficult to scale when multiple components fetch data.
✅ Optimized with Suspense (React 18 Approach)
import { Suspense } from "react";
import { fetchUserData } from "./userAPI";
const userResource = fetchUserData();
function UserProfile() {
const user = userResource.read();
return <h1>Welcome, {user.name}!</h1>;
}
export default function App() {
return (
<Suspense fallback={<div>Loading user...</div>}>
<UserProfile />
</Suspense>
);
}
✅ How it works:
fetchUserData()
creates a resource that Suspense can handle.- No manual loading states—Suspense automatically handles the delay.
- Cleaner, more declarative code with fewer side effects.
3️⃣ Using Suspense with React Query
React Query doesn’t use Suspense by default, but enabling it is easy.
📌 Example: Fetching Data with React Query (Without Suspense)
import { useQuery } from "@tanstack/react-query";
function UserProfile() {
const { data: user, isLoading } = useQuery(["user"], fetchUserData);
if (isLoading) return <div>Loading user...</div>;
return <h1>Welcome, {user.name}!</h1>;
}
🔴 Downsides:
- Still requires manual
isLoading
checks. - Extra conditional logic for handling UI states.
✅ Optimized with Suspense in React Query
Enable Suspense in React Query by passing { suspense: true }
:
import { Suspense } from "react";
import { useQuery } from "@tanstack/react-query";
function UserProfile() {
const { data: user } = useQuery(["user"], fetchUserData, { suspense: true });
return <h1>Welcome, {user.name}!</h1>;
}
export default function App() {
return (
<Suspense fallback={<div>Loading user...</div>}>
<UserProfile />
</Suspense>
);
}
✅ How it works:
- React Query automatically suspends rendering until data is ready.
- No need for
isLoading
checks—Suspense manages it. - Loading state is controlled at a higher level instead of inside components.
🛠 When to Use Suspense for Data Fetching?
✅ Good for:
✔️ Fetching critical data before rendering UI
✔️ Avoiding excessive re-renders due to loading states
✔️ Keeping UI clean & declarative
❌ Avoid if:
- You need more control over loading behavior.
- The API returns errors often (Suspense doesn’t handle errors natively).
Real-World Use Cases for Suspense
✅ Lazy-loading UI components dynamically
✅ Fetching user data before showing a dashboard
✅ Rendering real-time data updates with React Query
✅ Optimizing large forms or modals that fetch data
Final Thoughts
🔹 React 18 improved Suspense by integrating it with data fetching.
🔹 Suspense + React Query creates a cleaner, more efficient way to handle async data.
🔹 No more loading spinners everywhere—just declarative, seamless UI transitions.
Start using Suspense in your React projects today!
💬 Have you tried Suspense for data fetching yet? Let’s discuss in the comments!