<?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[I Wonder Why]]></title><description><![CDATA[Exploring the questions behind software, systems, and the technology we use every day. Understanding how things actually work, one question at a time.]]></description><link>https://i-wonder-why.hashnode.dev</link><image><url>https://cdn.hashnode.com/uploads/logos/66ebf01141be21533977d7e8/332c9bf1-4302-4610-8503-3c9d8b86416c.jpg</url><title>I Wonder Why</title><link>https://i-wonder-why.hashnode.dev</link></image><generator>RSS for Node</generator><lastBuildDate>Sat, 19 Sep 2026 15:37:30 GMT</lastBuildDate><atom:link href="https://i-wonder-why.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Before You Add Another Dynamic Import in Next.js, Read This]]></title><description><![CDATA[I remember using dynamic imports for the first time, long back. This was the Pages Router era, with no Server/Client Component split, no RSC module graphs. One fine evening, I was optimising the most-]]></description><link>https://i-wonder-why.hashnode.dev/dynamic-imports-nextjs</link><guid isPermaLink="true">https://i-wonder-why.hashnode.dev/dynamic-imports-nextjs</guid><category><![CDATA[Next.js]]></category><category><![CDATA[webdev]]></category><category><![CDATA[React]]></category><dc:creator><![CDATA[Ravi Anand]]></dc:creator><pubDate>Wed, 16 Sep 2026 19:51:34 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/66ebf01141be21533977d7e8/5f79c1ca-cb4f-4cdc-b825-465c2de3ec31.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I remember using dynamic imports for the first time, long back. This was the Pages Router era, with no Server/Client Component split, no RSC module graphs. One fine evening, I was optimising the most-visited page of our company's product, which was notoriously slow. I tried everything, ranging from lazy loading below-the-fold components, using <strong>content-visibility: auto,</strong> to using Intersection Observer for particularly heavy sections and a lot more tricks. But they only had a marginal impact on the performance score.</p>
<p>And then I stumbled upon an article mentioning how we can defer loading an entire module initially. At first, it felt similar to deferring a script, but not quite. I tried it out for a few components on the above-mentioned page and boom - a straight 25% reduction in first-load JS and a substantial decrease in the page's bundle size, as observed with the next bundle analyzer. It felt almost magical after the struggle to squeeze out minuscule performance gains.</p>
<p>A few days ago, I was optimizing an ad landing page and noticed the <code>next/dynamic</code> calls scattered across the component tree. I recalled the 'eureka' moment described earlier. And I was so sure of my assumption as to how a dynamic import works. The route already had <code>export const dynamic = 'force-static'</code> set, so I started questioning whether those dynamic imports were earning their place.</p>
<p>I converted a few components from <code>next/dynamic</code> back to static imports. The result: a marginal bump in performance score, and a more interesting drop in <strong>Cumulative Layout Shift (CLS)</strong>. It contradicted my mental model.</p>
<p>That sent me digging into how Next.js actually handles dynamic imports.</p>
<p>The mental model most of us default to: dynamic import → smaller initial bundle → better performance. But it is incomplete in a manner that isn't visible until you actually encounter the issue.</p>
<p><code>next/dynamic</code> is a combination of <code>React.lazy()</code> and <code>Suspense</code>. In a client component, it splits the imported component into a separate chunk, deferred until needed. Code-splitting 101.</p>
<p>But server components are a little tricky. The <strong>RSC compiler</strong> maintains separate server and client module graphs, and Next.js's own docs say that when a server component dynamically imports a client component, automatic code splitting isn't supported. So "dynamic import → smaller bundle" can simply not hold, depending on where the import sits in the tree.</p>
<p>And if the component is needed immediately anyway, there's a cost either way. With <code>ssr: false</code> or a <code>loading</code> fallback, you get a blank or placeholder state on first paint, then the real content popping in once the chunk loads. That's my guess for the source of the CLS I was seeing. I kept it simple and used static imports, which put the components in the initial server-rendered HTML from the start, no pop-in required.</p>
<p>This tells me that dynamic imports aren't a performance optimization by themselves. They're a loading strategy and a way to sequence when and where code loads, not a guarantee that less code ships or renders faster.</p>
<p>Another example is a heavy modal component which has a multi-step signup form that opens after clicking on a CTA button. You have two choices: defer loading the Signup modal until interaction (might increase the INP), or bundle it upfront.</p>
<p>Still narrowing down the exact mechanism behind the CLS improvement. If you've debugged something similar with next/dynamic in the App Router, I'd like to hear what you found.</p>
<p>Peace out :)</p>
]]></content:encoded></item></channel></rss>