ProNextJS
    Loading
    lesson

    Intro to Suspense and Streaming in Next.js

    Jack HerringtonJack Herrington

    There are many tools available for communicating between the client and server in a Next.js application. One of these tools is Suspense, which can be used to handle asynchronous operations like data fetching.

    In this section, we'll learn about Suspense by working with an interactive stock ticker app:

    stock ticker example app

    Currently, the app has a long loading time due to artificial delays in the data fetching process. We'll explore how to use Suspense to improve the user experience by providing feedback during the loading process.

    Starting Code Overview

    Let's break down the existing code for our stock ticker app and identify the bottlenecks. This can be found in the 05-suspense-and-diy-streaming directory of the course repository.

    The main component is a dashboard displaying a list of stocks. This dashboard currently fetches stock data using a getStocks function.

    // src/app/dashboard.tsx
    
    import { getStocks } from "@/lib/getStocks";
    
    export default async function Dashboard() {
      const stocks = await Promise.all(await getStocks());
    
      return (
        <div className="grid grid-cols-3 gap-4 mt-5">
          {stocks.map(({ name, ui }) => (
            <div key={name}>
              <div className="font-bold text-3xl">{name}</div>
              <div>{ui}</div>
            </div>
          ))}
        </div>
      );
    }
    

    The getStocks function generates fake stock data with artificial delays to simulate network requests, and each of the promises resolves to a stock object that includes a component:

    // inside lib/getStocks.tsx
    
    "use server";
    import type { StockInfo } from "@/types";
    import StockWithCounter from "@/components/stock-with-counter";
    
    const STOCKS = ["ARPL", "GLOG", "MDFT", "AMZO", "FBL", "TSLR"];
    
    async function generateFakeStock(
      name: string,
      delay: number
    ): Promise<StockInfo> {
      // Simulate a delay of at least 1 second
      await new Promise((resolve) =>
        setTimeout(resolve, Math.random() * delay + 1000)
      );
    
      const price = Math.random() * 1000 + 100;
      return {
        name,
        price,
        ui: <StockWithCounter name={name} price={price} />,
      };
    }
    
    export const getStocks = () => {
      return STOCKS.map((id) => generateFakeStock(id, 3000));
    };
    

    Right now, the Dashboard component waits for all promises to resolve before rendering. This leads to a noticeable delay, especially with the artificial lag we've introduced.

    The Solution: Suspense

    So how do we make this better for the user? The answer is Suspense!

    Suspense is a mechanism in React for handling asynchronous operations like data fetching. Instead of blocking the entire page while waiting for data, we can use Suspense to display a fallback UI. This fallback could be a loading indicator, a placeholder, or even some initial content, improving the perceived performance and making the app feel more responsive.

    Your challenge is to use Suspense to enhance the user experience by providing feedback during the data loading process. We'll go over the solution in the next video. Good luck!

    Transcript