React Suspense Boundary Error Handling - Errors Not Caught

I'm using React Suspense boundaries in my application, but when an error occurs in a component wrapped by Suspense, the error boundary doesn't catch it and the app crashes. The error shows "Error: A component suspended while rendering, but no fallback UI was provided" even though I have a Suspense boundary with a fallback. How can I properly handle errors in Suspense boundaries?

Solution

The issue is that Suspense boundaries only catch suspending (loading) states, not errors. To handle errors in components that use Suspense, you need to wrap your Suspense boundary with an Error Boundary.

Here's the correct pattern:

import { Suspense } from 'react';
import { ErrorBoundary } from 'react-error-boundary';

function ErrorFallback({ error, resetErrorBoundary }) {
  return (
    <div role="alert">
      <h2>Something went wrong:</h2>
      <pre>{error.message}</pre>
      <button onClick={resetErrorBoundary}>Try again</button>
    </div>
  );
}

function App() {
  return (
    <ErrorBoundary
      FallbackComponent={ErrorFallback}
      onError={(error, errorInfo) => {
        console.log('Error caught by boundary:', error, errorInfo);
      }}
    >
      <Suspense fallback={<div>Loading...</div>}>
        <DataComponent />
      </Suspense>
    </ErrorBoundary>
  );
}

Key points:

  • Error boundaries catch errors, Suspense catches loading states
  • Always wrap Suspense with ErrorBoundary for complete error handling
  • Use react-error-boundary library for better error boundary functionality
  • The error boundary will catch both synchronous errors and promise rejections

This pattern ensures that both loading states and errors are properly handled in your application.

Alternative #1

If you're using React 18+ with concurrent features, you might want to use the built-in error handling with useErrorBoundary hook for more granular control:

import { Suspense, useErrorBoundary } from 'react';
import { ErrorBoundary } from 'react-error-boundary';

function DataComponent() {
  const { resetBoundary } = useErrorBoundary();
  
  // Your async data fetching logic here
  const data = useAsyncData(); // This might throw or suspend
  
  return (
    <div>
      {data.map(item => (
        <div key={item.id}>{item.name}</div>
      ))}
    </div>
  );
}

function App() {
  return (
    <ErrorBoundary
      FallbackComponent={ErrorFallback}
      onReset={() => {
        // Reset logic here
      }}
    >
      <Suspense fallback={<div>Loading data...</div>}>
        <DataComponent />
      </Suspense>
    </ErrorBoundary>
  );
}

This approach gives you more control over error recovery and allows components to trigger error boundary resets programmatically.

Alternative #2

For server-side rendering scenarios, you might need to handle errors differently. The issue often occurs when using Suspense with SSR because the server can't suspend in the same way as the client.

import { Suspense } from 'react';
import { ErrorBoundary } from 'react-error-boundary';

function SSRDataComponent() {
  // Use try-catch for SSR compatibility
  try {
    const data = getServerData(); // This might throw
    return <div>{data}</div>;
  } catch (error) {
    // Handle error gracefully in SSR
    return <div>Error loading data</div>;
  }
}

function App() {
  return (
    <ErrorBoundary FallbackComponent={ErrorFallback}>
      <Suspense fallback={<div>Loading...</div>}>
        <SSRDataComponent />
      </Suspense>
    </ErrorBoundary>
  );
}

In SSR, you should:

  • Use try-catch blocks for error handling
  • Avoid throwing promises in server components
  • Handle errors gracefully without crashing the server
  • Use client-side Suspense for actual async operations
Alternative #3

If you're dealing with multiple nested Suspense boundaries, you need to ensure each level has proper error handling:

function NestedSuspenseApp() {
  return (
    <ErrorBoundary FallbackComponent={ErrorFallback}>
      <Suspense fallback={<div>Loading main content...</div>}>
        <MainContent />
        
        <ErrorBoundary FallbackComponent={ErrorFallback}>
          <Suspense fallback={<div>Loading sidebar...</div>}>
            <Sidebar />
          </Suspense>
        </ErrorBoundary>
        
        <ErrorBoundary FallbackComponent={ErrorFallback}>
          <Suspense fallback={<div>Loading footer...</div>}>
            <Footer />
          </Suspense>
        </ErrorBoundary>
      </Suspense>
    </ErrorBoundary>
  );
}

This pattern:

  • Provides granular error handling for each section
  • Prevents one component's error from crashing the entire app
  • Allows different fallback UIs for different sections
  • Maintains better user experience with partial loading states
Last modified: October 1, 2025
Stay in the loop
Subscribe to our newsletter to get the latest articles delivered to your inbox