<?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[Frontend Tech Blogs]]></title><description><![CDATA[Frontend performance deep dives — memory leaks, GC, frame drops &amp; more. Real-world fixes, profiling tips, and bottleneck breakdowns to help you ship faster,]]></description><link>https://blogs.raiyanrazi.dev</link><generator>RSS for Node</generator><lastBuildDate>Tue, 02 Jun 2026 16:58:07 GMT</lastBuildDate><atom:link href="https://blogs.raiyanrazi.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Script | Async | Defer – Why You Should Care?]]></title><description><![CDATA[A few months ago in my current company, we launched a new marketing page for our React web app. It was beautifully designed, loaded with animations, third-party scripts, and a lot of excitement from the team. But shortly after launch, the excitement ...]]></description><link>https://blogs.raiyanrazi.dev/script-async-defer-why-you-should-care</link><guid isPermaLink="true">https://blogs.raiyanrazi.dev/script-async-defer-why-you-should-care</guid><category><![CDATA[optimization]]></category><category><![CDATA[Web Development]]></category><category><![CDATA[webdev]]></category><category><![CDATA[React]]></category><category><![CDATA[performance]]></category><dc:creator><![CDATA[raiyan razi]]></dc:creator><pubDate>Tue, 19 Nov 2024 18:30:00 GMT</pubDate><content:encoded><![CDATA[<p>A few months ago in my current company, we launched a new marketing page for our React web app. It was beautifully designed, loaded with animations, third-party scripts, and a lot of excitement from the team. But shortly after launch, the excitement turned into confusion.</p>
<p>Why?</p>
<p>Because our <strong>First Contentful Paint (FCP)</strong> was <em>terrible</em>.</p>
<hr />
<h2 id="heading-the-problem-a-blank-screen-and-a-frustrated-pm">😫 The Problem: A Blank Screen and a Frustrated PM</h2>
<blockquote>
<p>"It takes almost <strong>3 seconds</strong> before anything even shows up."<br />– Our PM, after checking the Lighthouse report</p>
</blockquote>
<p>Our app had everything:</p>
<ul>
<li><p>A React hydration setup for the hero section.</p>
</li>
<li><p>A third-party analytics library. - Google Analytics</p>
</li>
<li><p>A customer support chat widget. - Intercom</p>
</li>
<li><p>A font loader script. - Google Font loader script</p>
<pre><code class="lang-xml">  <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"preconnect"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://fonts.googleapis.com"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"preconnect"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://fonts.gstatic.com"</span> <span class="hljs-attr">crossorigin</span>&gt;</span>
</code></pre>
</li>
<li><p>And, a fancy scroll animation library.</p>
</li>
</ul>
<blockquote>
<p>So, what did we do? - Removed all these scripts. 😝 - Nope, Just Kidding</p>
</blockquote>
<hr />
<h2 id="heading-digging-into-root-cause-of-the-problem">Digging into root cause of the problem -</h2>
<p>All of those were being loaded like this in our <code>index.html</code>:</p>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://cdn.analytics.com/track.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"/scroll-animations.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://chat.support.io/widget.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
</code></pre>
<p>No <code>async</code>. No <code>defer</code>. Just plain ol’ blocking <code>&lt;script&gt;</code> tags.</p>
<p>The browser was <strong>blocking rendering</strong> until all scripts were downloaded, parsed, and executed. That meant our LCP image, headings, and layout paint were <strong>delayed</strong>.</p>
<hr />
<h2 id="heading-digging-deeper">Digging Deeper</h2>
<p>We ran a Lighthouse audit. Here's what stood out:</p>
<ul>
<li><p><strong>First Contentful Paint</strong>: ~2.8s</p>
</li>
<li><p><strong>Main Thread Blocking Time</strong>: High</p>
</li>
<li><p><strong>Scripts</strong>: Loaded sequentially before any paint</p>
</li>
</ul>
<p>You can read more about the important web-vitals <a target="_blank" href="https://web.dev/articles/vitals#core-web-vitals">here</a></p>
<p>The waterfall chart made it obvious:<br />The browser was stuck downloading and executing JS while the user stared at a white screen.</p>
<p>We realized something had to change. We needed to load those scripts in a <strong>non-blocking</strong> way. That’s when we dove into:</p>
<hr />
<h2 id="heading-understanding-script-async-and-defer">Understanding <code>script</code>, <code>async</code>, and <code>defer</code></h2>
<p>Before we picked the right attributes, we had to actually understand what each of them meant — beyond just StackOverflow one-liners.</p>
<p>Here’s how we broke it down for ourselves over coffee:</p>
<blockquote>
<p><strong>“Okay team, imagine the browser is reading your HTML like a book. Now someone yells ‘Hey, run this JavaScript!’—what happens next?”</strong></p>
</blockquote>
<h3 id="heading-what-we-found">🔎 What we found:</h3>
<ul>
<li><p><code>&lt;script&gt;</code><br />  This is the default — the bossy one.<br />  It <em>stops everything</em> mid-sentence, makes the browser download and execute it, and only then continues reading.</p>
<blockquote>
<p>“Nope, not going anywhere until I finish this!”</p>
</blockquote>
</li>
<li><p><code>&lt;script async&gt;</code><br />  This one multitasks.<br />  The browser keeps reading the HTML while the script downloads in the background.<br />  But as <em>soon as</em> it's ready, it <strong>interrupts everything</strong> to execute it.</p>
<blockquote>
<p>“I don’t care what you’re doing — I’m ready, let’s go!”</p>
</blockquote>
</li>
<li><p><code>&lt;script defer&gt;</code><br />  The polite one.<br />  It also downloads while the HTML is being read, but waits until the whole DOM is parsed before it runs.<br />  Plus, <strong>it respects order</strong> — if you have three defer scripts, they’ll run one after the other.</p>
<blockquote>
<p>“I’ll wait my turn. You finish your book first.”</p>
</blockquote>
</li>
</ul>
<hr />
<h2 id="heading-choosing-the-right-tool-real-scripts-real-choices">Choosing the Right Tool: Real Scripts, Real Choices</h2>
<p>Once we understood what each script loading strategy actually <em>did</em>, we huddled around our performance dashboard and started profiling our script usage like detectives.</p>
<p>Every script was costing us time. But not all scripts were equally guilty.</p>
<p>So, we broke them down—<strong>one by one</strong>—and matched them with the loading strategy that made the most sense. Here's what that looked like:</p>
<hr />
<h3 id="heading-1-third-party-tracking-analyticsjs">1. <strong>Third-Party Tracking (analytics.js)</strong></h3>
<p>This was our visitor analytics script. It helped us track page views, clicks, and user journeys. Important for marketing—but completely irrelevant to rendering the UI.</p>
<p>Yet... we were loading it using a plain <code>&lt;script&gt;</code>, blocking everything. That made zero sense.</p>
<h4 id="heading-our-thought-process">🧠 Our Thought Process:</h4>
<ul>
<li><p>❌ Doesn’t affect the page visually.</p>
</li>
<li><p>❌ Doesn’t touch the DOM.</p>
</li>
<li><p>❌ Doesn’t need to run before the first paint.</p>
</li>
<li><p>✅ Just needs to send data <em>eventually</em>.</p>
</li>
</ul>
<p>So, we switched to:</p>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">defer</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://cdn.analytics.com/track.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
</code></pre>
<p>👉 <code>defer</code> made sure it loaded in the background and executed only <em>after</em> the browser had painted the first content. Non-blocking. Lightweight. Perfect.</p>
<hr />
<h3 id="heading-2-scroll-animation-library-scroll-animationsjs">2. <strong>Scroll Animation Library (scroll-animations.js)</strong></h3>
<p>This one was a little trickier. It handled fade-ins and parallax effects when users scrolled through sections. It absolutely needed the DOM to be fully parsed — otherwise it would error out trying to attach to elements that didn’t yet exist.</p>
<p>Originally, it too was blocking the render with a regular <code>&lt;script&gt;</code>, and it was a big file. Result: users were staring at a blank screen waiting for a <em>scroll effect script</em> to load. Not cool.</p>
<h4 id="heading-our-thought-process-1">🧠 Our Thought Process:</h4>
<ul>
<li><p>✅ Needed DOM to exist</p>
</li>
<li><p>❌ Didn’t need to run immediately</p>
</li>
<li><p>❌ Shouldn’t block painting</p>
</li>
</ul>
<p>We chose:</p>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">defer</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"/scroll-animations.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
</code></pre>
<p>👉 <code>defer</code> ensured it only executed once the DOM was fully available. It waited politely in line — just how we wanted it.</p>
<hr />
<h3 id="heading-3-support-chat-widget-chatsupportiohttpchatsupportio">3. <strong>Support Chat Widget (</strong><a target="_blank" href="http://chat.support.io"><strong>chat.support.io</strong></a><strong>)</strong></h3>
<p>This one sat in the bottom-right corner and allowed users to chat with support. It injected a floating widget and opened a live session on click. Cool? Yes. Essential to <em>initial load</em>? Not at all.</p>
<p>In fact, most users didn’t use it unless they were stuck or had a question. So why were we forcing every user to wait for it?</p>
<h4 id="heading-our-thought-process-2">🧠 Our Thought Process:</h4>
<ul>
<li><p>❌ Didn’t touch critical UI</p>
</li>
<li><p>❌ Didn’t depend on DOM content</p>
</li>
<li><p>✅ Could be loaded whenever ready</p>
</li>
</ul>
<p>We flipped the switch:</p>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">async</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://chat.support.io/widget.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
</code></pre>
<p>👉 <code>async</code> allowed it to download <em>in parallel</em>, and execute as soon as it finished. It didn’t block HTML parsing. It didn’t delay rendering. And it still showed up right on time when needed.</p>
<hr />
<h2 id="heading-tldr-treat-your-scripts-like-team-members">TL;DR: Treat Your Scripts Like Team Members</h2>
<p>Each script plays a role—but not every script deserves a front-row seat.</p>
<ul>
<li><p><strong>Critical to DOM?</strong> Use <code>defer</code></p>
</li>
<li><p><strong>Can run whenever?</strong> Use <code>async</code></p>
</li>
<li><p><strong>Need to block rendering?</strong> Ask yourself <em>why</em>, and change it if possible</p>
</li>
</ul>
<p>This conscious shift in script loading gave us measurable wins — and more importantly, a smoother experience for our users.</p>
<hr />
<h2 id="heading-what-we-learned-about-script-tags-the-hard-way">What We Learned About Script Tags (The Hard Way)</h2>
<p>When you're building a modern React app or any frontend application, it's easy to forget that <strong>how you load your scripts matters</strong>. Especially when third-party tools sneak into your page. Every script can cost you precious milliseconds – and your user’s patience.</p>
<p><code>defer</code> is usually the safest and best choice for scripts that depend on the DOM but don’t need to block rendering.<br /><code>async</code> is ideal for scripts that can run whenever they finish loading – especially those that don’t interact with the DOM.</p>
<p>So the next time you’re adding a <code>&lt;script&gt;</code> tag, ask yourself:</p>
<blockquote>
<p><em>Does this really need to block my page?</em></p>
</blockquote>
<p>Choose wisely. Your FCP (and your users) will thank you.</p>
<hr />
<h2 id="heading-keep-an-eye-on-how-much-time-it-is-taking-for-your-page-to-parse-the-html-doc">Keep an eye on how much time it is taking for your page to parse the HTML Doc</h2>
<p>During our deep dive into what was slowing down our React app’s First Contentful Paint, we stumbled across something we hadn’t really paid much attention to before:</p>
<blockquote>
<p><strong>The time it takes just to parse the HTML document.</strong></p>
</blockquote>
<p>We’d always focused on JavaScript bundle size, lazy loading components, and image optimization — all important.<br />But this? This was subtle. Yet critical.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1748636667121/446e6833-f877-4fec-882c-5bc8dd51cbc2.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-how-you-can-spot-it">🛠 How You Can Spot It</h3>
<p>Want to catch this in your own app?</p>
<p>Here’s how to do it:</p>
<ol>
<li><p>Open Chrome DevTools → <strong>Performance tab</strong></p>
</li>
<li><p>Hit the "⏺ Record" button</p>
</li>
<li><p>Reload your page</p>
</li>
<li><p>Look for the <strong>“Parse HTML”</strong> activity in the flame chart</p>
</li>
<li><p>Observe:</p>
<ul>
<li><p>How often it's interrupted</p>
</li>
<li><p>Whether any scripts are pausing it</p>
</li>
<li><p>If rendering is delayed because of it</p>
</li>
</ul>
</li>
</ol>
<p>If your HTML parsing is constantly <strong>getting paused by script execution</strong>, that’s a sign your <code>&lt;script&gt;</code> tags are in the way — and it’s time to rethink how you're loading them.</p>
<p>Read more about it <a target="_blank" href="https://developer.chrome.com/docs/devtools/performance/overview">here</a></p>
<hr />
<h3 id="heading-thank-you-for-reading">❤️ Thank You for Reading ❤️</h3>
]]></content:encoded></item></channel></rss>