Mahamudul Hasan Rubel
HomeAboutProjectsSkillsExperienceBlogPhotosContact
Mahamudul Hasan Rubel

Senior Software Engineer crafting high-performance web applications and SaaS platforms.

Navigation

  • Home
  • About
  • Projects
  • Skills
  • Experience
  • Blog
  • Photos
  • Contact

Get in Touch

Available for senior/lead roles and consulting.

bd.mhrubel@gmail.comHire Me

© 2026 Mahamudul Hasan Rubel. All rights reserved.

Built with using Next.js 16 & Tailwind v4

Back to Blog
Next.jsReactJune 20, 20264 min read

Streaming and Suspense in Next.js: Optimize Your Page Load

Master streaming and Suspense in Next.js to drastically improve perceived performance. Learn how to stream UI components for a faster, responsive experience.

Next.jsReactPerformanceWeb DevelopmentFrontendTypeScript
A close-up view of a laptop displaying a search engine page.

When we launched our new dashboard last quarter, the initial page load time was consistently hovering around 1.2 seconds. Users were staring at a blank white screen while the server fetched data for three different API endpoints before rendering anything. By implementing streaming and Suspense in Next.js, we cut the time-to-first-byte perception down to roughly 200ms by letting the UI render incrementally.

Why blocking the main thread hurts

In a standard Next.js setup, if your page relies on a heavy async server component, the entire page waits for that promise to resolve. This "all-or-nothing" approach is the enemy of user experience. You don't want your header and navigation blocked just because the user's activity feed is taking an extra half-second to pull from your database.

We first tried moving the heavy data fetching into a useEffect hook on the client side. That fixed the initial render, but it introduced a nasty layout shift and forced us to manage complex loading states manually. It was a mess. Switching to native React Suspense boundaries allowed us to keep our data fetching on the server while still getting that snappy, app-like feel.

Implementing streaming and Suspense in Next.js

To start streaming, you need to wrap your slow components in a Suspense boundary. Next.js handles the rest automatically. When the server encounters a component wrapped in Suspense, it sends the loading fallback immediately and then streams the result of the component once the data is ready.

Here is how we structured a typical product detail page:

TSX
// app/products/[id]/page.tsx
import { Suspense } from CE9178">'react';
import ProductInfo from CE9178">'@/components/ProductInfo';
import Reviews from CE9178">'@/components/Reviews';
import ReviewsSkeleton from CE9178">'@/components/ReviewsSkeleton';

export default async function ProductPage({ params }: { params: { id: string } }) {
  return (
    <main>
      <h1>Product Details</h1>
      {/* This renders immediately */}
      <ProductInfo id={params.id} />

      {/* This streams in later */}
      <Suspense fallback={<ReviewsSkeleton />}>
        <Reviews id={params.id} />
      </Suspense>
    </main>
  );
}

By separating the "fast" data from the "slow" data, the user gets the core content of the page instantly. The Reviews component, which might involve a complex join in your database, streams in as soon as it's ready.

Avoiding common pitfalls

While streaming is powerful, don't over-engineer it. If you wrap every single list item in a Suspense boundary, you’ll end up with "waterfall" hell, where the browser is trying to handle dozens of tiny, fragmented streams.

We had to rethink our component architecture that survives a growing team in Next.js to ensure that our boundaries were placed at logical, high-level breakpoints. Just like when we discussed fetching data in a React component the right way, the goal is to fetch as close to the component that needs it as possible.

Managing state during streaming

One thing that still trips up developers is where to put the loading state. If you find yourself struggling with complex state logic, remember that React props and state: Where your data should live is a rule that applies even when streaming. Don't try to pass data from a streaming component back up to the parent; keep the data local to the suspended component.

Also, be aware of how this interacts with your data layer. If you are struggling with stale data, make sure you understand the nuances of caching and revalidation in the Next.js App Router: A practical guide. Streaming doesn't bypass your cache; it just changes when that cached data is rendered to the user.

FAQ: Common hurdles

Q: Does streaming work with Client Components? A: Yes, but the Suspense boundary must be defined in a Server Component or a parent component. You can't wrap a Client Component in Suspense and expect it to stream the data fetching if that fetch happens inside the client component's useEffect.

Q: Can I nest Suspense boundaries? A: Absolutely. We often nest them for complex dashboards, with a high-level boundary for the main layout and inner boundaries for specific data-heavy widgets.

Q: What happens if the stream fails? A: If a component within a Suspense boundary throws an error, the error will bubble up to the nearest error.js boundary. Keep your error boundaries granular to prevent one broken widget from crashing the entire page.

Final thoughts

I'm still experimenting with how much granularity is too much when streaming. Sometimes, I find that a slightly slower initial load is better than a page that "pops" and shifts content too many times as different sections stream in. It’s a delicate balance between technical performance and perceived stability. Start by identifying your slowest data fetch and wrapping just that one component. You’ll be surprised at how much it changes the feel of your site.

Back to Blog

Similar Posts

Scrabble tiles spelling 'online store' on a rustic wooden background.
Next.jsReactJune 20, 20264 min read

Next.js Partial Prerendering: Optimizing Dynamic E-commerce Feeds

Next.js Partial Prerendering (PPR) lets you mix static and dynamic UI in one route. Learn how to optimize your e-commerce product feeds for instant loading.

Read more
Close-up of JavaScript code on a laptop screen, showcasing programming in progress.
ReactNext.jsJune 20, 20264 min read

Profiling and fixing a slow React render: A Practical Guide

Profiling and fixing a slow React render is easier when you stop guessing. Learn how to use React DevTools to find bottlenecks and optimize your app.

Read more
Detailed view of a server rack with a focus on technology and data storage.
ReactNext.jsJune 20, 20264 min read

Server components vs client components: A practical guide

Server components vs client components guide: learn how to choose the right React rendering strategy in Next.js to boost performance and simplify state.

Read more