Mahamudul Hasan Rubel
HomeAboutProjectsSkillsExperienceBlogCoursesPhotosContact
Mahamudul Hasan Rubel

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

Navigation

  • Home
  • About
  • Projects
  • Skills
  • Experience
  • Blog
  • Courses
  • 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
PerformanceJune 25, 20264 min read

Server-Side Streaming: Lowering TTFB and Boosting LCP with Node.js

Master Server-Side Streaming to slash your Time to First Byte. Learn how to use Node.js ReadableStreams to deliver content faster and improve LCP metrics.

Node.jsWeb PerformanceSSRStreamingLCPFrontend ArchitecturePerformanceWeb VitalsFrontend

My team spent three weeks chasing a mysterious latency spike that kept our Time to First Byte (TTFB) hovering around 800ms. We were rendering a complex dashboard on the server, waiting for three separate API calls to resolve before sending a single byte of HTML to the browser.

The fix wasn't a faster database or more cache layers; it was changing how we delivered the payload. By implementing Server-Side Streaming, we started pushing the shell of our page to the browser immediately, letting the client begin parsing while the backend finished fetching the heavy data.

Why Server-Side Streaming Matters for Performance

In a standard Server-Side Rendering (SSR) setup, the server acts like a bottleneck. It performs a "stop-the-world" dance: fetch, compute, render, and only then respond. Your user sits staring at a blank screen while your Node.js process does the heavy lifting.

Server-Side Streaming breaks this cycle. By using Node.js ReadableStreams, you can send the <head> and the layout skeleton to the client as soon as the initial request hits the server. As your data dependencies resolve, you stream the remaining chunks into the response. This approach is a game-changer for LCP optimization because the browser can start fetching critical CSS and fonts while the content is still being prepared.

The Wrong Turn: Chasing Micro-Optimizations

Before we committed to streams, we tried to optimize our data fetching by parallelizing our Promise.all() calls. It worked—we saved about 150ms—but it didn't solve the core UX problem. The browser was still idle during the initial request phase.

We also looked into TTFB Optimization: Using Resource Hints for Connection Warming to see if we could shave off time at the network layer. Resource hints are great, but they are a band-aid if your server is effectively "sleeping" while it builds a 500KB HTML string. We needed to stop waiting and start streaming.

Implementing Node.js Streams

If you're using Express or a similar framework, you're likely sending a full string via res.send(). To move to streaming, you need to transition to res.write().

Here’s a simplified version of how we handle this using Readable streams:

JAVASCRIPT
const { Readable } = require(CE9178">'stream');

function streamPage(res, dataPromise) {
  res.write(CE9178">'<html><head><title>Dashboard</title></head><body>');
  res.write(CE9178">'<div id="sidebar">Loading...</div>');

  // Create a readable stream for the main content
  const contentStream = new Readable({
    read() {}
  });

  dataPromise.then(data => {
    contentStream.push(CE9178">`<div id="main">${renderContent(data)}</div>`);
    contentStream.push(CE9178">'</body></html>');
    contentStream.push(null); // End the stream
  });

  contentStream.pipe(res, { end: false });
}

This approach allows the browser to receive the document head and the sidebar instantly. The main content arrives once the data is ready. You’ll notice the end: false option in the pipe—this is crucial because it keeps the response open while you append the final data chunks.

Balancing Streams and Hydration

One thing we learned the hard way: if you stream HTML, you have to be careful with your hydration strategy. If your frontend framework (React, Vue, etc.) expects the entire DOM to be present at load, you might run into issues with reconciliation. We had to move toward Selective Hydration and Islands Architecture for Better TBT to ensure that the parts of the page we streamed first could be hydrated independently of the slower, data-heavy components.

Without a thoughtful hydration strategy, you risk "layout shift" when the streamed content finally injects into the DOM. We mitigated this by reserving fixed-height containers for the async portions of the page.

Measuring the Impact

After deploying our streaming implementation, our TTFB dropped from a shaky 800ms to a consistent 120ms. The LCP metric improved by roughly 40%, because the browser could parse the CSS and start building the DOM tree while the backend was still busy.

I’m still not entirely satisfied with how we handle error states in a streaming response. If a data fetch fails halfway through the stream, you’ve already sent half a page of valid HTML. We’re currently looking into Server-Timing API for INP Optimization: Debugging Backend Latency to better correlate when our streams stall versus when the backend is simply slow.

Streaming isn't a silver bullet. It adds complexity to your response handling and requires you to think about your page as a series of components rather than a monolithic block. But for any dashboard or content-heavy site, the performance gains are worth the architectural shift. Next time, I’d like to experiment with HTTP/2 Server Push alongside these streams to see if we can push critical assets even faster.

Back to Blog

Similar Posts

PerformanceJune 22, 20264 min read

Resource Prioritization: Using Fetch Priority for LCP Optimization

Resource Prioritization using the Fetch Priority API can significantly improve your LCP. Learn how to signal browser importance to boost your load times.

Read more
PerformanceJune 24, 20264 min read

Selective Hydration and Islands Architecture for Better TBT

Selective Hydration combined with Islands Architecture slashes Total Blocking Time. Learn how to optimize your SSR apps for a snappier, more responsive experience.

Read more
PerformanceJune 24, 20264 min read

View Transitions API and Content-Visibility: Faster Page Navigation

Master the View Transitions API and content-visibility to create instantaneous navigation. Learn how these tools improve perceived performance in real apps.

Read more