Clean β’ Professional
Modern web apps often deal with asynchronous operations such as fetching data, loading components, or waiting for resources. React provides powerful built-in features β Suspense and Concurrent Mode (now part of Reactβs concurrent rendering features) β to make these experiences smoother, faster, and more user-friendly.
React Suspense is a feature that lets you delay rendering part of your UI until some asynchronous operation (like loading data or a component) is complete.
Think of Suspense as a loading boundary β it tells React:
βDonβt show this part of the UI until itβs ready β and in the meantime, show a fallback (like a spinner or message).β
Basic Syntax
<Suspense fallback={<div>Loading...</div>}>
<SomeAsyncComponent />
</Suspense>
Suspense: The wrapper component that handles the waiting state.fallback: The UI (like a loader or message) to display while the content is loading.SomeAsyncComponent: The component being loaded asynchronously (e.g., lazy-loaded).Example: Lazy Loading Components
Reactβs React.lazy() and Suspense work together for code splitting β loading components only when needed.
import React, { Suspense, lazy } from "react";
const About = lazy(() => import("./About")); // Lazy-load component
function App() {
return (
<div>
<h1>Welcome to My React App</h1>
<Suspense fallback={<p>Loading About Page...</p>}>
<About />
</Suspense>
</div>
);
}
export default App;
Here,
About is loaded only when needed.Loading About Pageβ¦).Concurrent Mode (introduced gradually as βConcurrent Featuresβ in React 18) improves how React renders updates in the background β making apps more responsive and interruptible.
Instead of rendering everything at once, React can:
In synchronous rendering, React blocks the UI until rendering finishes.
In concurrent rendering, React can start, pause, and continue rendering work β optimizing whatβs shown to the user first.
This helps:
Example: Transitions with useTransition
React 18 introduced the useTransition() hook to mark some updates as non-urgent, allowing React to handle them concurrently.
import React, { useState, useTransition } from "react";
function SearchList({ items }) {
const [query, setQuery] = useState("");
const [isPending, startTransition] = useTransition();
const filtered = items.filter((item) =>
item.toLowerCase().includes(query.toLowerCase())
);
const handleChange = (e) => {
const value = e.target.value;
startTransition(() => {
setQuery(value);
});
};
return (
<div>
<input type="text" onChange={handleChange} placeholder="Search..." />
{isPending && <p>Updating list...</p>}
<ul>
{filtered.map((item) => (
<li key={item}>{item}</li>
))}
</ul>
</div>
);
}
export default SearchList;
Here,
useTransition() lets React treat filtering as a low-priority task.isPending indicates the transition state.Together, Suspense and Concurrent Rendering make React apps:
Example (with data fetching libraries like React Query or Relay):
<Suspense fallback={<LoadingSpinner />}>
<UserProfile />
</Suspense>
Here, the <UserProfile /> component fetches data asynchronously, and the Suspense boundary ensures the user sees a smooth loading experience while waiting.
| Feature | Purpose | Example |
|---|---|---|
| Suspense | Handles loading states for async content | React.lazy, data fetching |
| Concurrent Rendering | Makes UI updates interruptible and smooth | useTransition, background updates |
| Fallback UI | Shown during async operations | <div>Loading...</div> |
| Key Benefit | Better performance and UX | Faster, smoother apps |