Skip to main content

ErrorBoundary and Suspense

Difference between Suspense and Error Boundary

ComponentWhat it doesWhen it triggers
<Suspense>Shows a fallback while the component isn't ready yet (for example, loading data or code).When the component "suspends" (throws a Promise).
<ErrorBoundary>Shows a fallback on error, when the component has crashed.When the component "crashes" (throws an Error).

React handles both a Promise and an Error the same way, through a "throw". The difference is what exactly was thrown:

  • a Promise means waiting (Suspense)
  • an Error means showing an error (ErrorBoundary)

How they work together

React goes top to bottom:

  1. A component can suspend the render (throw a Promise) -> React looks for the nearest <Suspense>.
  2. If a component crashes (throws an Error) -> React looks for the nearest <ErrorBoundary>.

So they can, and should, be nested inside each other so that different situations (loading and errors) are handled correctly.


Example of a correct combination

javascript
import { Suspense } from "react"; import ErrorBoundary from "./ErrorBoundary"; import { UserProfile } from "./UserProfile"; function App() { return ( <ErrorBoundary fallback={<h2>Error loading data</h2>}> <Suspense fallback={<h2>Loading profile...</h2>}> <UserProfile /> </Suspense> </ErrorBoundary> ); }

What happens:

  • if UserProfile isn't loaded yet (for example, React.lazy() or a Suspense-enabled request), the fallback from Suspense shows ("Loading...");
  • if UserProfile throws an error (for example, throw new Error()), the fallback from ErrorBoundary shows ("Error loading data").

An example with React.lazy()

javascript
const UserProfile = React.lazy(() => import("./UserProfile"));

Here React.lazy() itself throws a Promise while loading, so Suspense "catches" that promise and shows its fallback.


An example of nested Suspense + ErrorBoundary

You can wrap individual UI areas:

javascript
function Dashboard() { return ( <div> <ErrorBoundary fallback={<h3>Error in stats</h3>}> <Suspense fallback={<p>Loading stats...</p>}> <StatsWidget /> </Suspense> </ErrorBoundary> <ErrorBoundary fallback={<h3>Error in profile</h3>}> <Suspense fallback={<p>Loading profile...</p>}> <ProfileWidget /> </Suspense> </ErrorBoundary> </div> ); }

If StatsWidget breaks, "Error in stats" shows, while ProfileWidget stays untouched. If StatsWidget is simply loading, "Loading stats..." shows. Everything is independent.


A beginner mistake: nesting it the other way

javascript
<Suspense fallback={<p>Loading...</p>}> <ErrorBoundary fallback={<p>Error!</p>}> <UserProfile /> </ErrorBoundary> </Suspense>

This also works, but there's a nuance: if an error happens before Suspense resolves the Promise, React might not have time to show "loading" and shows "error" right away instead.

So ErrorBoundary usually has to be on the outside, to catch any errors inside the Suspense zone. Though sometimes (for example, with SSR or split loading) it's the other way around.


Summary: who is responsible for what

SituationWho handles itWhat it shows
The component is loading (lazy, data)Suspensea loading fallback
The component threw an error (throw new Error())ErrorBoundaryan error fallback
An error outside React (an event, async code)-needs a manual try/catch
Both situations are possibleCombinedSuspense while loading -> ErrorBoundary on error

javascript
<ErrorBoundary fallback={<ErrorUI />}> <Suspense fallback={<LoadingUI />}> <AsyncComponent /> </Suspense> </ErrorBoundary>

This way:

  • Loading -> LoadingUI
  • Error -> ErrorUI
  • Successful result -> AsyncComponent

A pattern for UI zones (best approach for large applications)

UI zoneErrorBoundarySuspense
The main pagea shared ErrorBoundarya shared Suspense
Individual widgetslocal ErrorBoundarieslocal Suspense
Critical zones (checkout, auth)separate ErrorBoundariesSuspense for data

Summary

Yes, ErrorBoundary and Suspense work great together. Use:

  • <Suspense> for loading (when the component isn't ready yet),
  • <ErrorBoundary> for errors (when the component has crashed).

Recommended nesting order:

javascript
<ErrorBoundary> <Suspense> <Component /> </Suspense> </ErrorBoundary>

This pattern:

  • isolates errors;
  • shows a fallback UI during loading and failures;
  • makes the interface resilient and smooth.

Short Answer

Interview ready
Premium

A concise answer to help you respond confidently on this topic during an interview.