Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why use Suspense in React Server Components

I feel like there is a gap in my understanding around SSR / SSG and <Suspense /> in React Server components, in the context of Next.js /app router.

Question: why would I ever use <Suspense /> in a React server component?

Situation: say I have a React server component /app/page.tsx:

import apiRoute from './constants/apiRoute'
import { DisplayData } from './components/DisplayData'

export default async function Home() {
  const resp = await fetch(apiRoute as string)
  const data = await resp.json()

  return (
    <main>
      <h1>My Site</h1>
      <DisplayData data={data} />
    </main>
  )
}

So my understanding is that this function is run on a server (Node.js). The server makes a request to apiRoute and then uses the data returned to generate a static HTML document (SSG). This HTML document (static asset) is then served by my application at /. ie a client that makes a request to http://<mydomain.com>/ will download this HTML file. (And then perhaps a <script /> tag will load a JS script from the Node.js server and hydrate the page/application in a subsequent request.)

Now if instead I did:

const resp = await fetch(apiRoute as string, {
  cache: 'no-cache',
})

The situation would be identical except that a fresh static HTML file would be created on the Node.js server each and every time a client makes a request to http://<mydomain.com>/ and that HTML file would be served to the client. ie if the data at apiRoute changed between client requests that updated data would be reflected in the static HTML document served to the client. As I understand it this is SSR.

But I often see people online doing stuff like this:

import { Suspense } from 'react'

export default async function Home() {
  const resp = await fetch(apiRoute as string)
  const data = await resp.json()

  return (
    <main>
      <h1>My Site</h1>
      <Suspense fallback={<div>Loading...</div>}>
        <DisplayData data={data} />
      </Suspense>
    </main>
  )
}

I don't understand why. To me it seems like the whole point is the HTML is created on the server before it is sent to the client and in that static HTML there is content that was populated (via React templating) by the data but the asset served and downloaded is now just static HTML (ie pretty much just text).

There should never be a "Loading" state. The client gets the whole static HTML file at once and the data is already in it. I could see why this would make sense in the context of a 'use client' component that uses JS to fetch data and while that HTTP request cycle is pending completion, it would show a loading state. But I am not understanding what is happening here with <Suspense /> fallbacks in server components. This Loading... state should never be shown. As soon as the client gets the HTML document, it's already got the data because the Node.js server had the data before it could finish creating the file that was to be served to the client; in both the SSR and SSG situations.

What am I missing here? What is the proper role of <Suspense /> in React server components? What advantages does it provide to me as the developer and to my application? I feel like I haven't found a use case for <Suspense /> in React server components, am I making my life harder?

like image 602
Casey Avatar asked Jul 02 '26 15:07

Casey


1 Answers

The greatest benefit of using suspense on RSCs is Streaming SSR. You can first render a "shell" with "holes" where dynamic components like your DisplayData can slide in. Then, on request, your Next.js server will send the shell first, and the server will gather and compile the dynamic data. These dynamic components' "boundaries" are defined with Suspense.

There's also a new experimental feature called Partial Prerendering for Next.js, or Island Architecture in general, where you can deploy those shells to CDNs near the user and stream the data to fill the dynamic components. Users get better first-paint results with interactivity.

like image 164
Sunghyun Cho Avatar answered Jul 05 '26 06:07

Sunghyun Cho