next.js: inconsistencies with streaming and `Suspense` on vercel

Verify canary release

  • I verified that the issue exists in the latest Next.js canary release

Provide environment information

Operating System:
      Platform: darwin
      Arch: arm64
      Version: Darwin Kernel Version 23.0.0: Mon May 22 22:50:46 PDT 2023; root:xnu-10002.0.40.505.5~4/RELEASE_ARM64_T8103
    Binaries:
      Node: 18.0.0
      npm: 8.6.0
      Yarn: 1.22.18
      pnpm: 7.19.0
    Relevant packages:
      next: 13.4.5-canary.9
      eslint-config-next: N/A
      react: 18.2.0
      react-dom: 18.2.0
      typescript: 5.1.3

Which area(s) of Next.js are affected? (leave empty if unsure)

App directory (appDir: true), Data fetching (gS(S)P, getInitialProps), Routing (next/router, next/navigation, next/link)

Link to the code that reproduces this issue or a replay of the bug

https://github.com/Fredkiss3/vercel-streaming-bug

To Reproduce

Go to the vercel-streaming-bug.vercel.app, check the wait checkbox and enter any pokemon name.

You may see that the page will wait for at least 2s before showing anything, you can retry with different names, and sometimes the fallback will be shown, but most of the time the page will block and immediatly return the new list of pokemons.

Describe the Bug

The bug is that in when deploying a next app to vercel, the Suspense fallback is not always shown and it blocks instead of showing a fallback. Even when the page clearly use edge runtime (so streaming should be enabled).

Expected Behavior

I tested on different providers to be sure :

On other providers, the fallback is always shown and the request is streamed correctly but on vercel sometimes it blocks before showing anything.

Which browser are you using? (if relevant)

No response

How are you deploying your application? (if relevant)

vercel

About this issue

  • Original URL
  • State: closed
  • Created a year ago
  • Reactions: 16
  • Comments: 38 (18 by maintainers)

Most upvoted comments

Let me check to see if this is indeed a Vercel issue, and if so, we can transfer this issue over to the Vercel GitHub Discussions and keep looking into this. Thanks for the reproduction!

Hey we found the issue I think so we’re rolling out a fix on production soon enough. Thanks for reporting and digging @Fredkiss3, super helpful.

This fix has been deployed! Thank you all for the helpful information and reproductions.

Thanks @leerob - If it does transfer somewhere else, please let us know where we can track it.

@YoussefKababe I’m not even using layouts, the fallback is in a page.tsx file. and i don’t even know how i could rearchitect the app to use templates.

Another thing : this issue only happen on vercel. so this is not simply a next.js issue… i think.

Ignore me, it’s still very much an issue. 🙃

This is happening to me in production now with nested suspense as well. Blocking the content download. Nice waterfall locally but issues on Vercel.

Related thread discussing the browser issue (which they’re seeing on multiple platforms): https://github.com/sveltejs/kit/issues/9154#issuecomment-1461169811

I seem to be having this same issue. Suspense/Streaming seems like such a core RSC feature to be broken, right? I couldn’t believe it was a bug and I spent hours trying to get it to work. Streaming is one of the biggest selling points of RSC!

Simple example below. This waits for 5 seconds on the server, rather than returning immediately and streaming in the Suspense content. https://demystifying-rsc.vercel.app/suspense-test/ Code:

import {Suspense} from "react";
export const dynamic='force-dynamic';

async function Delay() {
  return new Promise(r=>{
    setTimeout(()=>r(<div>Delayed Content: {Date.now()}</div>),5000);
  });
}
export default async()=>{
  return <Suspense fallback={<div>Loading...</div>}><Delay/></Suspense>
}

I’m having an issue where I have a component wrapped in suspense with a fallback, the component fetches data with an artificial delay, on navigating to the page it looks for a loading.js page as per NextJS docs and if none is found it blocks the page render until the data is fetched. However, if I reload the page suspense does work. An example is below.

// /dashboard.js

import { Suspense } from "react";
import PostList from "./postList";
import Header from './header';
import Link from "next/link";

const Dashboard = async () => {
  return (
    <div className="p-8">
      <Header />
      <Suspense fallback={<div>Loading...</div>}>
        <PostList />
      </Suspense>
    </div>
  );
};

export default Dashboard;

and

// /dashboard/postList.js

const PostList = async () => {
  const fetchDataWithDelay = async () => {
    const res = await fetch(
      "https://jsonplaceholder.typicode.com/posts?userId=3",
      {
        cache: "no-store",
      }
    );
    const data = await res.json();

    return new Promise((resolve) => {
      setTimeout(() => {
        resolve(data);
      }, 3000);
    });
  };

  const data = await fetchDataWithDelay();

  return (
    <ul className="flex flex-col gap-2">
      {data.map((post) => (
        <li
          key={post.id}
          className="flex flex-col border rounded-md border-slate-500 p-4"
        >
          <h4 className="text-lg font-medium">{post.title}</h4>
          <p className="text-slate-500">{post.body}</p>
        </li>
      ))}
    </ul>
  );
};

export default PostList;