<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Untitled Publication]]></title><description><![CDATA[Untitled Publication]]></description><link>https://oddisy.hashnode.dev</link><generator>RSS for Node</generator><lastBuildDate>Fri, 04 Sep 2026 23:04:17 GMT</lastBuildDate><atom:link href="https://oddisy.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Understanding the Waterfall Effect in Next.js (and How to Fix It)]]></title><description><![CDATA[Why Do Next.js Websites Load Slowly?
Building fast, responsive websites is every developer’s goal. But sometimes, a website that should load in 5 seconds ends up taking 15 seconds or more.
One common reason for this in Next.js is the waterfall effect...]]></description><link>https://oddisy.hashnode.dev/understanding-the-waterfall-effect-in-nextjs-and-how-to-fix-it</link><guid isPermaLink="true">https://oddisy.hashnode.dev/understanding-the-waterfall-effect-in-nextjs-and-how-to-fix-it</guid><category><![CDATA[Next.js]]></category><category><![CDATA[performance]]></category><category><![CDATA[React]]></category><category><![CDATA[optimization]]></category><category><![CDATA[Web Development]]></category><category><![CDATA[Frontend Development]]></category><category><![CDATA[JavaScript]]></category><dc:creator><![CDATA[Ogunleye Odunayo]]></dc:creator><pubDate>Fri, 22 Aug 2025 04:04:13 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1755835438265/86cdedca-e905-4281-b474-9fe81d704732.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-why-do-nextjs-websites-load-slowly">Why Do Next.js Websites Load Slowly?</h1>
<p>Building fast, responsive websites is every developer’s goal. But sometimes, a website that should load in <strong>5 seconds</strong> ends up taking <strong>15 seconds</strong> or more.</p>
<p>One common reason for this in Next.js is the <strong>waterfall effect</strong>. This happens when some API calls are made <strong>sequentially</strong> instead of <strong>in parallel</strong>, slowing everything down.</p>
<p>In this article, I’ll break down the <strong>waterfall effect</strong> with a simple analogy, explain why it affects performance, and share practical ways to fix it.</p>
<hr />
<h2 id="heading-what-is-the-waterfall-effect">What is the Waterfall Effect?</h2>
<p>The <strong>waterfall effect</strong> occurs when each API call waits for the previous one to finish before starting. Instead of loading resources in parallel, requests stack up one after another like water falling step by step.</p>
<p>This makes your site <strong>slower</strong> and increases <strong>time to first render</strong>, leading to poor user experience and higher bounce rates.</p>
<hr />
<h2 id="heading-a-restaurant-analogy">A Restaurant Analogy 🍽️</h2>
<p>Let’s make this simple. Imagine you and three friends (<strong>3 API calls</strong>) go to a restaurant.</p>
<p>Normally, the waiter should take all orders at once and deliver the meals together. That saves time.</p>
<p>But in the <strong>waterfall effect</strong> scenario:</p>
<ol>
<li><p>The waiter takes the <strong>first order</strong>, prepares it for <strong>5 minutes</strong>, but doesn’t deliver it.</p>
</li>
<li><p>Then takes the <strong>second order</strong>, prepares it for <strong>5 minutes</strong>, and still doesn’t deliver it.</p>
</li>
<li><p>Finally takes the <strong>third order</strong>, prepares it for <strong>5 minutes</strong>, and then delivers all meals together.</p>
</li>
</ol>
<p>Now, instead of waiting <strong>5 minutes total</strong>, you end up waiting <strong>15 minutes</strong>.</p>
<p>That’s how the <strong>waterfall effect</strong> delays your website.</p>
<ul>
<li><p>If one API call takes <strong>500ms</strong>,</p>
</li>
<li><p>Three sequential calls take <strong>500ms × 3 = 1500ms</strong>.</p>
</li>
</ul>
<p>👉 Bad performance.</p>
<p>➡️ Example:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">//❌ Sequential (Waterfall Effect)</span>
<span class="hljs-keyword">const</span> users = <span class="hljs-keyword">await</span> fetch(<span class="hljs-string">"/api/users"</span>).then(<span class="hljs-function"><span class="hljs-params">res</span> =&gt;</span> res.json());
<span class="hljs-keyword">const</span> posts = <span class="hljs-keyword">await</span> fetch(<span class="hljs-string">"/api/posts"</span>).then(<span class="hljs-function"><span class="hljs-params">res</span> =&gt;</span> res.json());
<span class="hljs-keyword">const</span> comments = <span class="hljs-keyword">await</span> fetch(<span class="hljs-string">"/api/comments"</span>).then(<span class="hljs-function"><span class="hljs-params">res</span> =&gt;</span> res.json());
</code></pre>
<p>Here:</p>
<ul>
<li><p>Request 2 waits for Request 1</p>
</li>
<li><p>Request 3 waits for Request 2</p>
</li>
</ul>
<p>Total time = sum of all requests (slow 🚶🏾‍♂️).</p>
<hr />
<h3 id="heading-when-waterfalls-are-useful">When Waterfalls Are <em>Useful</em></h3>
<p>Sometimes, sequential requests are necessary.</p>
<p>📌 <strong>Example:</strong></p>
<ul>
<li><p>Fetch a user’s profile → get their ID</p>
</li>
<li><p>Use the ID to fetch their list of friends</p>
</li>
</ul>
<pre><code class="lang-javascript"><span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">fetchUserData</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-comment">// Step 1: Fetch user profile</span>
  <span class="hljs-keyword">const</span> userProfile = <span class="hljs-keyword">await</span> fetch(<span class="hljs-string">'/api/user-profile'</span>).then(<span class="hljs-function"><span class="hljs-params">res</span> =&gt;</span> res.json());
  <span class="hljs-comment">// Step 2: Use the user ID to fetch their friends</span>
  <span class="hljs-keyword">const</span> friends = <span class="hljs-keyword">await</span> fetch(<span class="hljs-string">`/api/friends/<span class="hljs-subst">${userProfile.id}</span>`</span>).then(<span class="hljs-function"><span class="hljs-params">res</span> =&gt;</span> res.json());
  <span class="hljs-comment">// Step 3: Use the friends list to fetch posts</span>
  <span class="hljs-keyword">const</span> posts = <span class="hljs-keyword">await</span> fetch(<span class="hljs-string">`/api/posts?friends=<span class="hljs-subst">${friends.map(f =&gt; f.id).join(<span class="hljs-string">','</span>)}</span>`</span>)
    .then(<span class="hljs-function"><span class="hljs-params">res</span> =&gt;</span> res.json());
  <span class="hljs-built_in">console</span>.log({ userProfile, friends, posts });
}
fetchUserData();
</code></pre>
<p>In the restaurant analogy, it’s like asking each person what they want to eat, but the three friends need to confirm sequentially whether the previous dish is good before placing their own order. The waiter can’t start preparing the next meal until the status of the previous one is confirmed.</p>
<hr />
<p>How to Fix the Waterfall Effect</p>
<p>The good news? You can fix this with <strong>parallel fetching</strong> and <strong>streaming</strong>.</p>
<h3 id="heading-1-using-promiseall">1. Using <code>Promise.all()</code></h3>
<p>With <code>Promise.all()</code>, all API calls run at the same time. The waiter takes <strong>all orders at once</strong>, prepares everything in parallel, and then delivers the meals together.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">fetchData</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> [data1, data2, data3] = <span class="hljs-keyword">await</span> <span class="hljs-built_in">Promise</span>.all([
    fetch(<span class="hljs-string">'/api/first'</span>).then(<span class="hljs-function"><span class="hljs-params">res</span> =&gt;</span> res.json()),
    fetch(<span class="hljs-string">'/api/second'</span>).then(<span class="hljs-function"><span class="hljs-params">res</span> =&gt;</span> res.json()),
    fetch(<span class="hljs-string">'/api/third'</span>).then(<span class="hljs-function"><span class="hljs-params">res</span> =&gt;</span> res.json())
  ]);

  <span class="hljs-built_in">console</span>.log(data1, data2, data3);
}
</code></pre>
<ul>
<li><p>✅ Faster than sequential calls</p>
</li>
<li><p>⚠️ Downside: If one API call is slow, all others wait until it finishes</p>
</li>
</ul>
<p>Example: Instead of <strong>500ms total</strong>, it may take <strong>700ms</strong> if one request lags behind.</p>
<hr />
<h3 id="heading-2-using-react-suspense">2. Using <strong>React Suspense</strong></h3>
<p>React Suspense solves the waiting problem. It still makes all requests at once using promise.all(), but instead of waiting for all responses, it <strong>streams results progressively.</strong> By streaming, you can prevent slow data requests from blocking your whole page. This allows the user to see and interact with parts of the page without waiting for all the data to load before any UI can be shown to the user. So, as regards the analogy, as soon as one meal is ready, it’s delivered without waiting for the others.<br />Streaming works well with React's component model, as each component can be considered a <em>chunk</em>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755883943457/a5230619-c66c-44bd-9294-a7c348f707c8.png" alt class="image--center mx-auto" /></p>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> { Suspense } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Page</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Suspense</span> <span class="hljs-attr">fallback</span>=<span class="hljs-string">{</span>&lt;<span class="hljs-attr">p</span>&gt;</span>Loading user...<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>}&gt;
        <span class="hljs-tag">&lt;<span class="hljs-name">User</span> /&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">Suspense</span>&gt;</span>

      <span class="hljs-tag">&lt;<span class="hljs-name">Suspense</span> <span class="hljs-attr">fallback</span>=<span class="hljs-string">{</span>&lt;<span class="hljs-attr">p</span>&gt;</span>Loading posts...<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>}&gt;
        <span class="hljs-tag">&lt;<span class="hljs-name">Posts</span> /&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">Suspense</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
}
</code></pre>
<ul>
<li><p>✅ Users see content faster</p>
</li>
<li><p>✅ Improves perceived performance</p>
</li>
</ul>
<p>🚀 Best for user experience</p>
<p>⚠️ <strong>Note:</strong> Suspense doesn’t work with <strong>client-side API SDKs</strong> (like Firebase). In such cases, you need to manage a <strong>loading state manually</strong> and stream each API’s data into the UI as it becomes available.</p>
<hr />
<h3 id="heading-3-using-loading-states-for-client-apis-eg-firebase">3. Using <strong>Loading States for Client APIs (e.g., Firebase)</strong></h3>
<p>When working with client-based APIs or SDKs (like <strong>Firebase</strong>), you can’t fully rely on React Suspense because it requires additional code and logic to integrate . Instead, you need to manage <strong>loading states</strong> manually, However this is not as optimized as react suspense because the components may re-render multiple times (initial state , loading state , and loaded state ) which can lead to performance issues on large or complex applications .</p>
<p>It is the-same as React Suspense but for client-based APIs.</p>
<pre><code class="lang-plaintext">import { useEffect, useState } from "react";
import { getDocs, collection } from "firebase/firestore";
import { db } from "./firebase";

function Posts() {
  const [posts, setPosts] = useState([]);
  const [loading, setLoading] = useState(true);

  useEffect(() =&gt; {
    async function fetchPosts() {
      const snapshot = await getDocs(collection(db, "posts"));
      const data = snapshot.docs.map(doc =&gt; doc.data());
      setPosts(data);
      setLoading(false);
    }
    fetchPosts();
  }, []);

  if (loading) return &lt;p&gt;Loading posts...&lt;/p&gt;;

  return (
    &lt;ul&gt;
      {posts.map((post, i) =&gt; (
        &lt;li key={i}&gt;{post.title}&lt;/li&gt;
      ))}
    &lt;/ul&gt;
  );
}
</code></pre>
<ul>
<li><p>✅ Works with client SDKs like Firebase</p>
</li>
<li><p>✅ Streams each API response into the UI using a <strong>loading state</strong></p>
</li>
</ul>
<p>⚠️ Requires additional codes and logic compared to Suspense</p>
<hr />
<p><strong>Key Takeaways</strong></p>
<ul>
<li><p><strong>Waterfall effect</strong> = sequential API calls = 🚶 slow websites.</p>
</li>
<li><p><strong>Promise.all()</strong> = parallel API calls = 🚴 faster websites.</p>
</li>
<li><p><strong>Suspense</strong> = progressive streaming = 🚀 best UX (but not for client SDKs).</p>
</li>
<li><p><strong>Loading states for client SDKs</strong> = stream results to UI manually when using APIs like Firebase.</p>
</li>
</ul>
<hr />
<h2 id="heading-conclusion">Conclusion</h2>
<p>Website performance is crucial for <strong>Next.js apps</strong>. A slow site not only frustrates users but also affects <strong>SEO ranking, engagement, and conversion rates</strong>.</p>
<p>By avoiding the <strong>waterfall effect</strong>, you ensure faster loading times and smoother experiences. Use <code>Promise.all()</code> for parallel fetching, and take advantage of <strong>React Suspense</strong> for progressive loading.</p>
<p>If you want your Next.js apps to feel <strong>snappy and responsive</strong>, remember:<br />👉 Don’t let your APIs fall like a waterfall.</p>
<p>✨ Thanks for reading! If you found this helpful, share it with another developer and let’s build faster web apps together.</p>
]]></content:encoded></item></channel></rss>