React Suspense
In this page:
<Suspense fallback={<p>Loading...</p>}>
<LazyComponent />
</Suspense>
What Suspense Does
Suspense lets a component pause rendering and show a fallback UI (like a spinner) instead, until whatever it's waiting for is ready. The most common, stable use of this today is React.lazy() for code-splitting, where Suspense shows a fallback while the component's code is still downloading.
Example: What Suspense Does
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
const LazyComponent = React.lazy(() =>
Promise.resolve({ default: () => <p>I loaded (simulated lazy import)</p> })
);
function App() {
return (
<React.Suspense fallback={<p>Loading...</p>}>
<LazyComponent />
</React.Suspense>
);
}
ReactDOM.createRoot(document.getElementById('root')).render(<App />);
</script>
</body>
</html>
One Fallback for Multiple Suspending Children
A single Suspense boundary can wrap several lazy-loaded (or otherwise suspending) children at once. The fallback shows if ANY of them aren't ready yet, and the actual content only appears once ALL of them have finished loading.
Example: One Fallback for Multiple Suspending Children
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
const ComponentA = React.lazy(() => Promise.resolve({ default: () => <p>A loaded</p> }));
const ComponentB = React.lazy(() => Promise.resolve({ default: () => <p>B loaded</p> }));
function App() {
return (
<React.Suspense fallback={<p>Loading both...</p>}>
<ComponentA />
<ComponentB />
</React.Suspense>
);
}
ReactDOM.createRoot(document.getElementById('root')).render(<App />);
</script>
</body>
</html>
Suspense Is Not a General Async Replacement
Suspense doesn't automatically work with any Promise or arbitrary async code — it requires something specifically built to integrate with it, like React.lazy for components, or a data-fetching library with explicit Suspense support.
A plain fetch() inside useEffect doesn't suspend on its own.
Example: Suspense Is Not a General Async Replacement
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
// This does NOT integrate with Suspense automatically:
function NormalFetcher() {
const [data, setData] = React.useState(null);
React.useEffect(() => { setData("loaded"); }, []);
return <p>{data || "loading (Suspense doesn't see this)"}</p>;
}
function App() {
return <React.Suspense fallback={<p>This fallback won't show for the above</p>}><NormalFetcher /></React.Suspense>;
}
ReactDOM.createRoot(document.getElementById('root')).render(<App />);
</script>
</body>
</html>
- Trying to use Suspense for regular data fetching (like a fetch() call in useEffect) — that's not what it's built for without a Suspense-compatible data library.
- Forgetting the fallback prop is required — Suspense needs to know what to show while waiting.
- Not understanding Suspense only works with things specifically designed to suspend (like React.lazy or Suspense-enabled data libraries), not arbitrary async code.
- Suspense lets a component tree show a fallback UI while something inside it isn't ready yet.
- The most common, stable use case is code-splitting with React.lazy, showing a fallback while a chunk downloads.
- Suspense for data fetching exists but requires a library specifically built to support it (like React Query's or Relay's Suspense integration).
- A single Suspense boundary can wrap multiple lazy/suspending children, showing one shared fallback.
Suspense for lazy-loaded components stable since React 16.6; Suspense for data fetching is a more recent, evolving feature (React 18+).
Chapter Quiz — Complete all 14 topics to unlock
0/14 topics done
Complete these topics first:
- Error Boundaries
- React Portals
- Modals using Portals (practical use)
- React Suspense
- Code Splitting with React.lazy
- Introduction to Server Components
- Introduction to Next.js (server-side React)
- Using React with TypeScript
- Scalable Folder Architecture
- Common React Design Patterns
- Component Documentation with Storybook
- Accessibility (a11y) in React
- i18n with react-i18next
- React Security Best Practices