DEV Community

Abhay Singh Kathayat
Abhay Singh Kathayat

Posted on

How to Use Suspense in React for Improved Asynchronous Rendering

Suspense in React

React Suspense is a powerful feature that allows developers to handle asynchronous rendering gracefully by displaying fallback UI while components or data are loading. It works hand-in-hand with React.lazy, Concurrent Rendering, and Data Fetching solutions like React Query, Relay, or custom implementations.


How Suspense Works

  1. Placeholder Fallback: Suspense wraps around components that need to wait for asynchronous operations. While waiting, it displays a fallback UI (e.g., a loading spinner).
  2. Automatic Rendering: Once the asynchronous operation completes, the real component or data is rendered.

Basic Syntax

import React, { Suspense } from "react";

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

function App() {
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <LazyComponent />
    </Suspense>
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode
  • React.Suspense: Wraps around lazy-loaded components.
  • fallback: Specifies the UI to show while waiting for the component to load.

Use Cases of Suspense

  1. Lazy Loading Components: Loading components dynamically with React.lazy.
  2. Data Fetching: Managing asynchronous data loading when combined with libraries like Relay or React Query.
  3. Concurrent Rendering: Optimizing rendering in concurrent React modes.

Example 1: Lazy Loading with Suspense

Without Suspense

import React from "react";
import HeavyComponent from "./HeavyComponent";

function App() {
  return (
    <div>
      <h1>Main App</h1>
      <HeavyComponent />
    </div>
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

The HeavyComponent loads synchronously, increasing the initial load time.

With Suspense

import React, { Suspense } from "react";

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

function App() {
  return (
    <div>
      <h1>Main App</h1>
      <Suspense fallback={<div>Loading Heavy Component...</div>}>
        <HeavyComponent />
      </Suspense>
    </div>
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

Now, the HeavyComponent loads only when required, showing a fallback during the loading process.


Example 2: Suspense with Multiple Lazy Components

import React, { Suspense } from "react";

const ComponentA = React.lazy(() => import("./ComponentA"));
const ComponentB = React.lazy(() => import("./ComponentB"));

function App() {
  return (
    <div>
      <h1>Main App</h1>
      <Suspense fallback={<div>Loading Components...</div>}>
        <ComponentA />
        <ComponentB />
      </Suspense>
    </div>
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

The fallback UI is displayed until both components finish loading.


Example 3: Suspense for Data Fetching (Experimental)

React Suspense is also useful for asynchronous data fetching when integrated with libraries like Relay or React Query.

Example with React Query

import React, { Suspense } from "react";
import { useQuery } from "react-query";

function DataComponent() {
  const { data } = useQuery("fetchData", fetchData);

  return <div>{data}</div>;
}

function App() {
  return (
    <Suspense fallback={<div>Loading data...</div>}>
      <DataComponent />
    </Suspense>
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

Error Handling with Suspense

If an error occurs during the loading of components or data, React Suspense does not provide built-in error handling. Use an Error Boundary for this purpose.

import React, { Suspense } from "react";

class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false };
  }

  static getDerivedStateFromError() {
    return { hasError: true };
  }

  render() {
    if (this.state.hasError) {
      return <div>Error loading component</div>;
    }
    return this.props.children;
  }
}

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

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

export default App;
Enter fullscreen mode Exit fullscreen mode

Best Practices for Using Suspense

  1. Simple Fallbacks: Use lightweight placeholders to avoid performance overhead.
  2. Combine with Error Boundaries: Ensure proper handling of component or data load failures.
  3. Chunk Intelligently: Divide your application into logical chunks for better performance.

Benefits of Suspense

  1. Improved User Experience: Ensures the app remains responsive by showing a fallback UI.
  2. Reduced Bundle Size: Works well with code splitting for optimized bundle sizes.
  3. Simplified Asynchronous Handling: Manages loading states in a more declarative way.

Advanced Use: Nested Suspense

You can nest multiple Suspense components for granular control over fallback states.

import React, { Suspense } from "react";

const ComponentA = React.lazy(() => import("./ComponentA"));
const ComponentB = React.lazy(() => import("./ComponentB"));

function App() {
  return (
    <div>
      <h1>Main App</h1>
      <Suspense fallback={<div>Loading Component A...</div>}>
        <ComponentA />
      </Suspense>
      <Suspense fallback={<div>Loading Component B...</div>}>
        <ComponentB />
      </Suspense>
    </div>
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

Limitations of Suspense

  1. Data Fetching Support: Experimental feature for fetching data directly.
  2. No Error Handling: Requires additional error handling mechanisms like Error Boundaries.
  3. Browser Compatibility: Requires modern browsers with support for ES6 modules and promises.

Conclusion

React Suspense is a versatile tool for managing asynchronous rendering in React applications. It simplifies handling loading states, enhances performance with lazy loading, and creates a smoother user experience when combined with code splitting and error handling techniques.


Top comments (0)