<?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[Optimizing Our Next.js Bundle Size]]></title><description><![CDATA[Optimizing Our Next.js Bundle Size]]></description><link>https://blogs.aadhavan-va.com</link><generator>RSS for Node</generator><lastBuildDate>Thu, 16 Apr 2026 20:24:31 GMT</lastBuildDate><atom:link href="https://blogs.aadhavan-va.com/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[What I Learned Optimizing Our Next.js Bundle Size (and How You Can Do It Too)]]></title><description><![CDATA[During one of our recent feature releases, we noticed something concerning: our initial page load bundle size had increased. As the application grows, this kind of creeping increase can negatively impact performance — especially for users on slower n...]]></description><link>https://blogs.aadhavan-va.com/what-i-learned-optimizing-our-nextjs-bundle-size</link><guid isPermaLink="true">https://blogs.aadhavan-va.com/what-i-learned-optimizing-our-nextjs-bundle-size</guid><category><![CDATA[bundlesize]]></category><category><![CDATA[Next.js]]></category><category><![CDATA[webpack]]></category><dc:creator><![CDATA[Aadhavan V A]]></dc:creator><pubDate>Thu, 04 Dec 2025 13:04:09 GMT</pubDate><content:encoded><![CDATA[<blockquote>
<h3 id="heading-during-one-of-our-recent-feature-releases-we-noticed-something-concerning-our-initial-page-load-bundle-size-had-increased-as-the-application-grows-this-kind-of-creeping-increase-can-negatively-impact-performance-especially-for-users-on-slower-networks-so-we-decided-it-was-time-to-investigate-and-optimize">During one of our recent feature releases, we noticed something concerning: our <strong>initial page load bundle size had increased</strong>. As the application grows, this kind of creeping increase can negatively impact performance — especially for users on slower networks. So, we decided it was time to investigate and optimize.</h3>
</blockquote>
<p><strong>Step 1: Checking the Build Output</strong></p>
<pre><code class="lang-bash">yarn build
</code></pre>
<p>This allowed me to see the bundle breakdown for each route in our Next.js application. While the output gave some high-level numbers, I wanted more visibility into <em>which packages</em> were increasing the size.</p>
<p><strong>Step 2: Analyzing With Bundle Analyzer</strong></p>
<p>To dig deeper, I used the Next.js <a target="_blank" href="https://www.npmjs.com/package/@next/bundle-analyzer">Bundle Analyzer</a> plugin. This tool visualizes everything included in the client bundle. Once the build completed, the analyzer made one issue very obvious:</p>
<p>The first load js shared by all is 1.16MB before lazy loading as you can see below</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1764851994350/68a753ee-8b32-4e4e-9804-d9d169457f06.png" alt class="image--center mx-auto" /></p>
<p>👉 The <strong>emoji-mart</strong> package we introduced for our new feature was taking up <strong>~100 KB gzipped</strong> just on the initial load.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1764851637542/727f4933-315e-4da6-ae31-df3f8ff6b56a.png" alt class="image--center mx-auto" /></p>
<p><strong>Step 4: Dynamic Import to the Rescue</strong></p>
<p>To fix this, I removed the default static import from the main component and created a custom React hook to dynamically import the library when the user interacts with the feature.</p>
<p>This approach gives us <strong>code-splitting</strong>, meaning the emoji picker is bundled separately and only downloaded when required.<br />Created a custom hook to load emoji-mart library on user click.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> useEmojiPicker = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">const</span> [emojiDataLoaded, setEmojiDataLoaded] = useState(<span class="hljs-literal">false</span>)
  <span class="hljs-keyword">const</span> [emojiData, setEmojiData] = useState&lt;<span class="hljs-built_in">any</span>&gt;(<span class="hljs-literal">null</span>)

  <span class="hljs-keyword">const</span> loadEmojiData = <span class="hljs-keyword">async</span> () =&gt; {
    <span class="hljs-keyword">if</span> (!emojiDataLoaded) {
      <span class="hljs-keyword">try</span> {
        <span class="hljs-keyword">const</span> data = <span class="hljs-keyword">await</span> <span class="hljs-keyword">import</span>(<span class="hljs-string">'@emoji-mart/data'</span>)
        setEmojiData(data.default)
        setEmojiDataLoaded(<span class="hljs-literal">true</span>)
      } <span class="hljs-keyword">catch</span> (error) {
        <span class="hljs-built_in">console</span>.error(<span class="hljs-string">'Failed to load emoji data:'</span>, error)
      }
    }
  }

  <span class="hljs-keyword">return</span> { emojiData, emojiDataLoaded, loadEmojiData }
}
</code></pre>
<pre><code class="lang-typescript"><span class="hljs-comment">// Parent Component</span>
<span class="hljs-keyword">const</span> { emojiDataLoaded, emojiData, loadEmojiData } = useEmojiPicker()

<span class="hljs-comment">// OnClickHandler</span>
<span class="hljs-keyword">const</span> onClickHandler = <span class="hljs-keyword">async</span> () {
    <span class="hljs-comment">// Load emoji data on first picker open</span>
    <span class="hljs-keyword">if</span> (!emojiDataLoaded) {
      <span class="hljs-keyword">await</span> loadEmojiData()
    }
}
</code></pre>
<p>By doing this during build time, the <strong>webpack</strong> will split the emoji-mart package into separate chunks and it is loaded when needed instead of first page load.</p>
<p><strong>Step 5: Reduced bundle size</strong></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1764852097826/8fb14759-42f2-408b-bec4-cb38de881957.png" alt class="image--center mx-auto" /></p>
<p>After applying the change, I ran yarn build again.</p>
<p>Result: 🎉</p>
<p>Our first page load bundle size dropped from <strong>1.16 MB → 1.05 MB</strong>.</p>
<p>It may not sound huge, but in performance work:</p>
<blockquote>
<p><strong>Small optimizations compound — and early wins help guide future improvements.</strong></p>
</blockquote>
<p><strong>Conclusion:</strong><br />This was just the first step in optimizing our growing codebase. Moving forward, we’ll continue</p>
<ul>
<li><p>Auditing unused dependencies</p>
</li>
<li><p>Applying lazy loading where possible</p>
</li>
<li><p>Monitoring bundle size for every new feature</p>
</li>
<li><p>Automating alerts when thresholds are exceeded</p>
</li>
</ul>
]]></content:encoded></item></channel></rss>