Next.js Discord

Discord Forum

Suspense fallback not showing in Layout.tsx

Answered
Blue swimming crab posted this in #help-forum
Open in Discord
Avatar
Blue swimming crabOP
I'm trying to show a loading state while an async component in my layout fetches data, but the Suspense fallback never shows up. Here's a minimal reproduction:

async function SlowComponent({ children }: { children: React.ReactNode }) {
  // Artificial delay to simulate slow data fetching
  await new Promise((resolve) => setTimeout(resolve, 3000));
  return <div>{children}</div>;
}

export default function TestLayout({ children }: TestLayoutProps) {
  return (
    <div className="h-screen w-screen">
      <Suspense fallback={<div>Loading...</div>}>
        <SlowComponent>{children}</SlowComponent>
      </Suspense>
    </div>
  );
}


Instead of showing the "Loading..." fallback during the 3-second delay, the page just stays blank. What am I missing here? How can I properly show a loading state while the async component in my layout is fetching?

Using:
- Next.js 14.2.12
- App Dir
- Server Components
Image
Answered by Asian black bear
Looks like you're using Safari. Seems like something to do with this:
https://bugs.webkit.org/show_bug.cgi?id=252413

If your fallback is minimal (e.g. just "Loading ...") Safari might just not paint that.
View full answer

2 Replies

Avatar
Asian black bear
Looks like you're using Safari. Seems like something to do with this:
https://bugs.webkit.org/show_bug.cgi?id=252413

If your fallback is minimal (e.g. just "Loading ...") Safari might just not paint that.
Answer
Avatar
Blue swimming crabOP
Thank you so much - this was brilliant! The WebKit bug report you linked helped me understand the root cause, and I found a neat workaround by playing into Safari's "heuristic".

Adding hidden text with sr-only (2 paragraphs worth) was enough to convince Safari that there's "meaningful content" to do first paint. Now loading state shows up perfectly in Safari too:

<div className="sr-only" aria-hidden="true">
  {Array.from({ length: 2 }).map((_, i) => (
    <p key={i}>Loading content...</p>
  ))}
</div>


Really appreciate you pointing me in the right direction! 🙏