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

    When it comes to communicating between the client and the server, Next.js App Writer is an embarrassment of riches when it comes to the tools that we can use to manage the connection between the client and the server to get data out to the client in cool new ways. And one of the things we haven't really talked about much yet in this section is using suspense. So let's take a look at this example application. We've got some stock tickers here effectively. You can click on these.

    There's our interactive dynamic components. We get the stock name and then the price. And it's nice. The only problem with this is that the actual time to get this is really long. If I go to about blank, and then I go back to local host, You can see it takes quite a while for it to come up, and that's because the data on the back end is being artificially laggy.

    So let's go take a look at this application. Our homepage simply just invokes this dashboard component. This dashboard component is a React server component. Currently it calls getStocks. That getStocks function gives us back an array of promises.

    Each promise resolves to one of these stocks. So what we're going to do is we're going to render out each one of those. Now, in this case, we await all of those promises to resolve and that's where the delay comes in. Well, let's take a look at how we render the stocks. Now, we've got the name.

    In this case, that'd be ARPL, Glog, MDFT. That's pretty easy. We just render that as text. But the component, this interactive component here, is actually coming in as UI. So we're getting this UI element back, and then we're just rendering that in place in this div.

    So let's go take a look at getStocks and see if we can understand a little bit more about how this is happening. So getStocks is defined over in lib. If you look down the bottom, we've got our getStocks. And what it does is it iterates through this array of stocks and then generates fake stocks for each one. The array is listed up at the top here on line five and generate fake stock is where it gets interesting.

    So generate fake stock takes a ticker symbol, the name and then the delay. So that's, you can make it as long as you want. And then randomly waits for that delay, but it's at least a second. And then when it's done, it returns the name and the price and it returns a UI element. So we're actually returning a UI element as part of this promise.

    And that UI element is this Stock with Counter. Stock with Counter is defined over in Components. It's a simple display component that also has a counter on it, just to show that this is a client component and it's interactive. All right, so how would you, well, maybe not speed this page up, but at least let people know that stuff is happening before they actually get the data. Because right now the page just kind of locks up.

    So what's a tool that we can use to give some more information to the customer that data is on the way? And hint, it's suspense. So why don't you try and use suspense to see if you can get this page to be non-blocking. Now I'll show you my solution in the next video.