<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Adam Bradley</title>
    <description>The latest articles on DEV Community by Adam Bradley (@adamdbradley).</description>
    <link>https://dev.to/adamdbradley</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F201941%2Fde802289-0ca7-4107-a773-fc69f0d2bbc7.jpg</url>
      <title>DEV Community: Adam Bradley</title>
      <link>https://dev.to/adamdbradley</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/adamdbradley"/>
    <language>en</language>
    <item>
      <title>Speculative Module Fetching: a Modern Approach to Faster App Interactivity</title>
      <dc:creator>Adam Bradley</dc:creator>
      <pubDate>Tue, 07 Mar 2023 16:03:28 +0000</pubDate>
      <link>https://dev.to/builderio/speculative-module-fetching-a-modern-approach-to-faster-app-interactivity-4g40</link>
      <guid>https://dev.to/builderio/speculative-module-fetching-a-modern-approach-to-faster-app-interactivity-4g40</guid>
      <description>&lt;p&gt;Speculative Module Fetching is an approach that optimizes app responsiveness by loading only the &lt;em&gt;possible&lt;/em&gt; points of user interactivity based on the current state of the app. As the user interacts with the app, it continues to speculate and fetch the next possible modules.&lt;/p&gt;

&lt;h2&gt;
  
  
  An Analogy: video downloading vs. streaming
&lt;/h2&gt;

&lt;p&gt;Before we dive in, let's compare the differences between downloading a two-hour movie and streaming it. When you download a video file to your device, you can't start watching it until the download is complete. For a multiple gigabyte video file, this can take a while.&lt;/p&gt;

&lt;p&gt;Thankfully, services like Netflix, Hulu, and YouTube are well aware of this issue. That's why they &lt;em&gt;stream&lt;/em&gt; content to your devices. When you start playing the movie, you stream only the first few seconds.&lt;/p&gt;

&lt;p&gt;As you watch the movie, the service continues to buffer the next few seconds you're about to watch. And if the user stops and never finishes streaming the movie, that's fine. It doesn't waste resources on the user's device or the service's network.&lt;/p&gt;

&lt;h2&gt;
  
  
  Speculative Module Fetching
&lt;/h2&gt;

&lt;p&gt;Qwik City applies the same streaming concept by anticipating which modules the user might interact with next, based on the content they are viewing.&lt;/p&gt;

&lt;p&gt;Rather than loading modules on-demand (as known as lazy-loading), Qwik City pre-populates the cache ahead of time with modules the user may need. This also differs from prefetching, where often the entire application’s JavaScript is downloaded using main-thread resources.&lt;/p&gt;

&lt;p&gt;Similar to video streaming, Qwik City fetches the &lt;em&gt;next&lt;/em&gt; modules ahead of time in a background thread as the user continues to interact with the application. This allows the application to grow in size and complexity while still maintaining fast startup, interactivity, and responsiveness.&lt;/p&gt;

&lt;p&gt;For instance, suppose that a page has a click listener on a button. When the page loads, the system ensures that the bundle for that click listener is downloaded and stored in the cache. When the user clicks the button and Qwik makes a request to the event listener's bundle, the bundle is already sitting in the cache, ready to execute.&lt;/p&gt;

&lt;p&gt;To take it one step further, it’s similar to like a video game quest. Choose your own adventure. As the user navigates the app, the app will cache what’s needed next.&lt;/p&gt;

&lt;h2&gt;
  
  
  Advantages from Resumability
&lt;/h2&gt;

&lt;p&gt;Since Qwik uses &lt;a href="https://www.builder.io/blog/resumability-vs-hydration"&gt;resumability, instead of hydration&lt;/a&gt;, the build creates many different entry points for the app, rather than a single &lt;code&gt;main.js&lt;/code&gt; script.&lt;/p&gt;

&lt;p&gt;The previous mindset has been that there’s only &lt;em&gt;one script&lt;/em&gt; that starts an application. And even if there’s lazy-loading with dynamic imports and the &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; is added just before &lt;code&gt;&amp;lt;/body&amp;gt;&lt;/code&gt;, this initial script that contains the framework still has to run in order for the entire application to work.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s---jUtx60X--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.builder.io/api/v1/image/assets%252FYJIGb4i01jvw0SRdL5Bt%252F0d2910a2db61476c97347c8ad08060b0%3Fwidth%3D705" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s---jUtx60X--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.builder.io/api/v1/image/assets%252FYJIGb4i01jvw0SRdL5Bt%252F0d2910a2db61476c97347c8ad08060b0%3Fwidth%3D705" alt="a diagram showing single entry vs multiple entries in frameworks." width="705" height="474"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With resumability, Qwik is able to &lt;em&gt;continue&lt;/em&gt; an application from the state that it was when it left the server (either SSR or SSG).&lt;/p&gt;

&lt;p&gt;Instead of a single entry that involves hydration to get up and running, there are many points at which the application can resume. In many cases, the app may never even need to start some parts or even the entire app.&lt;/p&gt;

&lt;h2&gt;
  
  
  Beyond Traditional Prefetching
&lt;/h2&gt;

&lt;p&gt;Due to Qwik’s unique &lt;a href="https://www.builder.io/blog/module-extraction-the-silent-web-revolution"&gt;module extraction&lt;/a&gt; primitive and &lt;a href="https://qwik.builder.io/docs/concepts/resumable/"&gt;resumability&lt;/a&gt;, pages startup and become interactive extremely fast without requiring any JavaScript. The next question, however, is if JavaScript isn’t required, how does interactivity even work?&lt;/p&gt;

&lt;p&gt;Traditional web development often resorts to prefetching and hydration, or a combination of both. Even though you may be lazy-loading per page, you still need the framework to hydrate the app. And when you require the framework in order for it to work, the user has a non-responsive app as the framework is downloading and hydrating. And for low-end mobile devices on real-world networks, this could be too long for the user to wait.&lt;/p&gt;

&lt;p&gt;Qwik, on the other hand, does &lt;em&gt;not&lt;/em&gt; use a traditional main-thread prefetching or lazy-loading approach, but rather takes advantage of service workers to execute Speculative Module Fetching.&lt;/p&gt;

&lt;h2&gt;
  
  
  Caching with a Service Worker
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API/Using_Service_Workers"&gt;Service workers&lt;/a&gt; are typically thought of as a way to make an application work offline. However, Qwik City takes advantage of the powerful features of service workers to provide Speculative module fetching and heavily caching modules on the user's device.&lt;/p&gt;

&lt;p&gt;The goal is not to download the entire application (which users probably won’t interact with most of it anyways), but rather to use a service worker to dynamically download what is possible to execute at that moment. By not downloading the entire application, resources are freed up to only request the small parts of the app a user could use for what they have on their screen at that time.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--9cmyogDn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.builder.io/api/v1/image/assets%252FYJIGb4i01jvw0SRdL5Bt%252F4b4b0f9bf89d46bb9934e40aea3f3cc4%3Fwidth%3D705" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--9cmyogDn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.builder.io/api/v1/image/assets%252FYJIGb4i01jvw0SRdL5Bt%252F4b4b0f9bf89d46bb9934e40aea3f3cc4%3Fwidth%3D705" alt="a diagram of main thread worker thread communication." width="705" height="428"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;An advantage of using a service worker is that it's also an extension of a &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API"&gt;worker&lt;/a&gt;, which runs in a background thread.&lt;/p&gt;

&lt;p&gt;Web workers make it possible to run a script operation in a background thread separate from the main execution thread of a web application. The advantage of this is that laborious processing can be performed in a separate thread, allowing the main (usually the UI) thread to run without being blocked/slowed down.&lt;/p&gt;

&lt;p&gt;By moving the downloading and caching logic to a service worker (which is a worker), we're able to essentially run the code in a background task, in order to not interfere with the main UI thread. By not interfering with the main UI we're able to help improve the performance of the application for end-users.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Speculative Module Fetching Works
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Page HTML loads without any hydration or framework JavaScript.&lt;/li&gt;
&lt;li&gt;Main thread sends to the service worker all of the possible user interactions on that page at that time.&lt;/li&gt;
&lt;li&gt;Service worker receives a message from the main thread of all possible interactions.&lt;/li&gt;
&lt;li&gt;Service worker fetches modules it does not already have stored in the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Cache"&gt;Cache API&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;When the user interacts with the app, dynamic imports pull from the pre-populated cache.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Reducing Network Waterfalls
&lt;/h2&gt;

&lt;p&gt;A network waterfall is when numerous requests happen one after another, like steps downstairs, rather than in parallel. Below is an exaggerated case of a less-than-ideal request waterfall.&lt;/p&gt;

&lt;p&gt;A waterfall of network requests usually hurt performance because it increases the time until all modules are downloaded, rather than each module starting to download at the same time.&lt;/p&gt;

&lt;p&gt;Below is an example with three modules: &lt;code&gt;A&lt;/code&gt;, &lt;code&gt;B&lt;/code&gt; and &lt;code&gt;C&lt;/code&gt;. Module &lt;code&gt;A&lt;/code&gt; imports &lt;code&gt;B&lt;/code&gt;, and &lt;code&gt;B&lt;/code&gt; imports &lt;code&gt;C&lt;/code&gt;. The HTML document is what starts the waterfall by first requesting Module A.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// module-a.js&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./module-b.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Module A&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// module-b.js&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./module-c.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Module B&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// module-c.js&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Module C&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- HTML page --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"module"&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"/module-a.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, when Module &lt;code&gt;A&lt;/code&gt; is first requested, the browser has no idea that it should also start requesting Module &lt;code&gt;B&lt;/code&gt; and &lt;code&gt;C&lt;/code&gt;. It doesn't even know it needs to start requesting Module &lt;code&gt;B&lt;/code&gt;, until AFTER Module &lt;code&gt;A&lt;/code&gt; has finished downloading. Basically, the browser doesn't know ahead of time what it should start to request, until &lt;em&gt;after&lt;/em&gt; each module has finished downloading.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--TiWC8rzk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.builder.io/api/v1/image/assets%252FYJIGb4i01jvw0SRdL5Bt%252F2dcc5c9ec0b2494092f43fea9d934866%3Fwidth%3D705" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--TiWC8rzk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.builder.io/api/v1/image/assets%252FYJIGb4i01jvw0SRdL5Bt%252F2dcc5c9ec0b2494092f43fea9d934866%3Fwidth%3D705" alt="Two types of network waterfalls" width="705" height="385"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;However, because our service worker contains a module graph generated from the manifest at build time, we already know all of the modules which &lt;em&gt;will&lt;/em&gt; be requested next. Qwik can even know which interactions the user will most likely use next with Real Metric Optimizations, but more on that later.&lt;/p&gt;

&lt;p&gt;So when either user interaction or requesting a bundle happens, the worker thread initiates the request for all of the bundles that it already knows will be requested. This way, we drastically reduce the time it takes to request all bundles.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dynamic Imports and Caching
&lt;/h2&gt;

&lt;p&gt;When Qwik requests a module, it uses the standardized dynamic &lt;code&gt;import()&lt;/code&gt;. For example, let's say a user interaction happened, requiring Qwik to execute a dynamic import for &lt;code&gt;/abc.js&lt;/code&gt;. The pseudo-code would look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/abc.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What's important here is that Qwik itself has no knowledge of a caching strategy. It's simply making an HTTP request for a URL. However, because we've installed a service worker, and the service worker is intercepting requests, and the service worker is able to inspect the URL and say, "hey look, this is a request for &lt;code&gt;/abc.js&lt;/code&gt;! This is one of our bundles! Let's first check to see if we already have this in the cache before we do an actual network request."&lt;/p&gt;

&lt;p&gt;This is where the power of the service worker and cache API comes in! Qwik first pre-populates the cache for modules the user may soon request within another thread. And better yet, if it's already cached, then there's no need for the browser to do anything.&lt;/p&gt;

&lt;p&gt;And remember, the service worker also knows which additional modules are imported by each module, so we can reduce the network waterfall by kicking off each request in parallel.&lt;/p&gt;

&lt;h2&gt;
  
  
  Avoiding Duplicate Requests
&lt;/h2&gt;

&lt;p&gt;It may be possible to fire off duplicate requests for the same resource. For example, let's say we want to import &lt;code&gt;module-a.js&lt;/code&gt;, and while that's being downloaded (which may take a short time or a very long time, we don't know), the user interacts with the app, which then decides to request and execute &lt;code&gt;module-a.js&lt;/code&gt;. Browsers will often fire off a second request for the same URL, which makes matters worse.&lt;/p&gt;

&lt;p&gt;The service worker approach can &lt;a href="https://qwik.builder.io/qwikcity/advanced/speculative-module-fetching/#avoiding-duplicate-requests"&gt;avoid this&lt;/a&gt; by identifying a request that is already in flight, waiting on the first request for &lt;code&gt;module-a.js&lt;/code&gt; to finish, and then cloning it for the second request.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Only one network request will happen&lt;/strong&gt;, even though numerous module imports may call for the same request/response.&lt;/p&gt;

&lt;h2&gt;
  
  
  Advantages of Understanding the Module Graph
&lt;/h2&gt;

&lt;p&gt;One of the unique features of the &lt;a href="https://qwik.builder.io/docs/advanced/optimizer/"&gt;Qwik Optimizer&lt;/a&gt; is its ability to provide a deep understanding of how an application works and how it can be best split apart. Unlike blindly lazy-loading entire chunks of code (usually split at the page level), the Optimizer can bundle at an extremely fine-grained level due to its &lt;a href="https://www.builder.io/blog/module-extraction-the-silent-web-revolution"&gt;module extraction&lt;/a&gt; primitive.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Clicked&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Click&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the above example, the button cannot be changed, so no templating or core rendering code is necessary. When the button is clicked, the app console logs "Clicked,” which is all that needs to be fetched ahead of time. Even Qwik's core library is not required in this case.&lt;/p&gt;

&lt;p&gt;Furthermore, when using &lt;a href="https://www.builder.io/blog/usesignal-is-the-future-of-web-frameworks"&gt;signals&lt;/a&gt;, very little JavaScript is needed because only the specific DOM node within the app needs to be updated, rather than re-rendering the entire component tree as is common in other frameworks.&lt;/p&gt;

&lt;p&gt;Most of Qwik's actual work is done at build-time, and its core feature is understanding that the click event code is not the component's rendering code. The above is a simple example, but this optimization can scale by minimizing the need to load most of the application's templates, interactions, and logic.&lt;/p&gt;

&lt;h2&gt;
  
  
  Advanced Bundling and Real Metric Optimizations
&lt;/h2&gt;

&lt;p&gt;Bundlers do a good job concatenating JavaScript. But they lack the context of how the application will actually be used out in the real-world, by real-users, on production servers.&lt;/p&gt;

&lt;p&gt;Since Qwik uses module extraction and resumability to create many fine-grained modules, it combines modules, which results in fewer requests. Instead of having one tiny module per interaction, Qwik can bundle them more efficiently.&lt;/p&gt;

&lt;p&gt;However, to take it to next level, Qwik can also bundle and prioritize modules depending on how users actually run the application. For example, if users of a product page most commonly click the Add to Cart button, and change the order quantity, then the bundler knows to group those modules together, and to make it the first module to cache on page load.&lt;/p&gt;

&lt;p&gt;But much more on Real Metric Optimizations feature later…&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Building a massive application, but without a massive amount of JavaScript, is quite a challenge. This is where we’ll continue to research ways to improve efficiently requesting, caching and bundling Qwik’s modules.&lt;/p&gt;

&lt;p&gt;If you want to know more about Qwik’s Speculative Module Fetch please take a look at our docs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://qwik.builder.io/qwikcity/advanced/speculative-module-fetching/"&gt;Speculative Module Fetching&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://speculative-modules-docs.qwik-docs.pages.dev/qwikcity/advanced/speculative-module-fetching/#parallelizing-network-requests"&gt;Parallelizing Network Requests&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://speculative-modules-docs.qwik-docs.pages.dev/qwikcity/advanced/speculative-module-fetching/#reducing-network-waterfalls"&gt;Reducing Network Waterfalls&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://speculative-modules-docs.qwik-docs.pages.dev/qwikcity/advanced/speculative-module-fetching/#avoiding-duplicate-requests"&gt;Avoiding Duplicate Requests&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  About me
&lt;/h2&gt;

&lt;p&gt;Hi! I’m &lt;a href="https://twitter.com/adamdbradley"&gt;Adam&lt;/a&gt;, Director of Technology at &lt;a href="https://www.builder.io/"&gt;Builder.io&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We make a way to drag + drop with your components to create pages and other CMS content on your site or app, &lt;a href="https://www.builder.io/blog/headless-cms-workflow"&gt;visually&lt;/a&gt;.&lt;br&gt;
So this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;BuilderComponent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;registerComponent&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="err"&gt;‘&lt;/span&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;io&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;react&lt;/span&gt;&lt;span class="err"&gt;’&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Hero&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Products&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="err"&gt;‘&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;my&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;components&lt;/span&gt;&lt;span class="err"&gt;’&lt;/span&gt;
&lt;span class="c1"&gt;// Dynamically render compositions of your components&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;MyPage&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;json&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;BuilderComponent&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c1"&gt;// Use your components in the drag and drop editor&lt;/span&gt;
&lt;span class="nx"&gt;registerComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Hero&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;registerComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Products&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Gives you this:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Y1qvC5Xj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/au5t8ti43cci5n6t6cfb.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Y1qvC5Xj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/au5t8ti43cci5n6t6cfb.gif" alt="Gif of Builder.io" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>performance</category>
      <category>javascript</category>
      <category>frontend</category>
    </item>
    <item>
      <title>Experimental Performance Insights Panel in Chrome DevTools</title>
      <dc:creator>Adam Bradley</dc:creator>
      <pubDate>Wed, 01 Mar 2023 18:11:45 +0000</pubDate>
      <link>https://dev.to/builderio/experimental-performance-insights-panel-in-chrome-devtools-4i2j</link>
      <guid>https://dev.to/builderio/experimental-performance-insights-panel-in-chrome-devtools-4i2j</guid>
      <description>&lt;p&gt;Have you tried Chrome DevTool’s Experimental Performance Insights panel? If not, let's explore some of its many slick features together.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flqsj7xjwplv70x90y13h.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flqsj7xjwplv70x90y13h.gif" alt="DevTools Performance Insights panel" width="427" height="308"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;While the existing Performance panel you may be familiar with has many (many) capabilities, it’s also quite overwhelming at the same time. The Performance panel suffers from trying to solve too many use cases, which is why the new experimental panel’s streamlined and simplified UI is a welcomed update.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enabling Experimental Performance Insights 🧑‍🔬
&lt;/h2&gt;

&lt;p&gt;If you don't already see the Performance Insights tab, you can enable it by going to More options icon -&amp;gt; More tools -&amp;gt; Performance Insights.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxrxhk6abz7514kf06wf3.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxrxhk6abz7514kf06wf3.gif" alt="Enabling Performance Insights" width="341" height="407"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Alternatively, you can also enable Performance Insights through the the DevTools Command Menu.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enable Throttling 🦥
&lt;/h2&gt;

&lt;p&gt;Before we start our first Performance recording, I recommend enabling Network and CPU throttling to get a better idea of what users are experiencing in the real world. Additionally, disabling cache also helps to ensure we’re testing more realistic conditions for first-time visitors.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5pejeaypk3l1wulmic08.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5pejeaypk3l1wulmic08.gif" alt="Performance Throttling" width="379" height="265"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Performance Recording 🔴
&lt;/h2&gt;

&lt;p&gt;Click the "Measure page load" button for DevTools to reload the page begin the performance recording. It’ll automatically stop the recording a couple of seconds after the page load has finished.&lt;/p&gt;

&lt;p&gt;Easily one of the best features of Performance Insights would have to be the playback feature. It allows you not only reply the entire performance recording, but also scrub and zoom into to exact moments within the record.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1np80sax0o2ypiyftzjk.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1np80sax0o2ypiyftzjk.gif" alt="Performance Recording Playback" width="427" height="269"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And making it even more powerful is how it visually lines up with the all the tasks the browser is performing at that moment, like network requests and rendering.&lt;/p&gt;

&lt;h2&gt;
  
  
  Insights and Details Panel 🧐
&lt;/h2&gt;

&lt;p&gt;Performance Insights is more than just tooling, but also goes further into helping pin-point the problem, and recommend some solutions.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Futg8o3foowca6raf7ult.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Futg8o3foowca6raf7ult.gif" alt="Insights and Details panel" width="271" height="405"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For example, the right-hand side lays out a timeline of what’s happened during the recording. Each section in the Insights panel provides a link to get more details.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try out the Experimental Performance Insights Panel!
&lt;/h2&gt;

&lt;p&gt;In short, the new Performance Insights panel provides a lot more information and is easier to use than the existing Performance tab. Give it a try and let us know how it goes. Thanks for reading!&lt;/p&gt;

&lt;h2&gt;
  
  
  About me
&lt;/h2&gt;

&lt;p&gt;Hi! I’m &lt;a href="https://twitter.com/adamdbradley" rel="noopener noreferrer"&gt;Adam&lt;/a&gt;, Director of Technology at &lt;a href="https://www.builder.io/" rel="noopener noreferrer"&gt;Builder.io&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We make a way to drag + drop with your components to create pages and other CMS content on your site or app, &lt;a href="https://www.builder.io/blog/headless-cms-workflow" rel="noopener noreferrer"&gt;visually&lt;/a&gt;.&lt;br&gt;
So this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;BuilderComponent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;registerComponent&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="err"&gt;‘&lt;/span&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;io&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;react&lt;/span&gt;&lt;span class="err"&gt;’&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Hero&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Products&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="err"&gt;‘&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;my&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;components&lt;/span&gt;&lt;span class="err"&gt;’&lt;/span&gt;
&lt;span class="c1"&gt;// Dynamically render compositions of your components&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;MyPage&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;json&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;BuilderComponent&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c1"&gt;// Use your components in the drag and drop editor&lt;/span&gt;
&lt;span class="nf"&gt;registerComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Hero&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;registerComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Products&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Gives you this:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fau5t8ti43cci5n6t6cfb.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fau5t8ti43cci5n6t6cfb.gif" alt="Gif of Builder.io" width="760" height="506"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1630989157567692800-526" src="https://platform.twitter.com/embed/Tweet.html?id=1630989157567692800"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1630989157567692800-526');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1630989157567692800&amp;amp;theme=dark"
  }



 &lt;/p&gt;

</description>
      <category>caching</category>
      <category>webperf</category>
      <category>webdev</category>
    </item>
    <item>
      <title>How To Improve Lighthouse Scores by Avoiding &lt;img&gt; Layout Shifts</title>
      <dc:creator>Adam Bradley</dc:creator>
      <pubDate>Thu, 23 Feb 2023 18:04:39 +0000</pubDate>
      <link>https://dev.to/builderio/how-to-improve-lighthouse-scores-by-avoiding-layout-shifts-14go</link>
      <guid>https://dev.to/builderio/how-to-improve-lighthouse-scores-by-avoiding-layout-shifts-14go</guid>
      <description>&lt;p&gt;One of the most common killers of Lighthouse scores, and also one of the easiest to solve, would have to be setting the correct aspect ratio on your images. Layout shifts can happen for many reasons, but here we’ll focus on how images can have a negative effect.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8qojtss063cz81vbjogr.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8qojtss063cz81vbjogr.gif" alt="Example of image loading changes the content layout" width="274" height="540"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Basically, it’s annoying to be reading content only to see the layout adjust and that same content moved to another location. This is why &lt;a href="https://developer.chrome.com/docs/lighthouse/overview/" rel="noopener noreferrer"&gt;Lighthouse&lt;/a&gt; and other performance measuring tools have a metric called &lt;a href="https://web.dev/cls/" rel="noopener noreferrer"&gt;Cumulative Layout Shift&lt;/a&gt;, or CLS.&lt;/p&gt;

&lt;p&gt;It’s pretty straightforward: if your page has CLS issues, your Lighthouse scores go down.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.builder.io%2Fapi%2Fv1%2Fimage%2Fassets%252FYJIGb4i01jvw0SRdL5Bt%252Fdfbbcb1d13154507a7e0ff9eea9bd8f1%3Fwidth%3D705" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.builder.io%2Fapi%2Fv1%2Fimage%2Fassets%252FYJIGb4i01jvw0SRdL5Bt%252Fdfbbcb1d13154507a7e0ff9eea9bd8f1%3Fwidth%3D705" alt="screenshot of Lighthouse CLS showing the main image of the document." width="705" height="291"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.builder.io%2Fapi%2Fv1%2Fimage%2Fassets%252FYJIGb4i01jvw0SRdL5Bt%252F0915ea4931654f6c84b8f2b796692879%3Fwidth%3D705" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.builder.io%2Fapi%2Fv1%2Fimage%2Fassets%252FYJIGb4i01jvw0SRdL5Bt%252F0915ea4931654f6c84b8f2b796692879%3Fwidth%3D705" alt="screenshot of Lighthouse CLS timeline." width="705" height="671"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Another challenge is that we’re also designing pages that are responsive. Meaning that the same content should look good on both desktop and mobile, so setting exact dimensions for images is usually not an option.&lt;/p&gt;

&lt;p&gt;Luckily, there are two recommended cross-browser approaches to solving image CLS issues:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;aspect-ratio&lt;/code&gt; CSS property on &lt;code&gt;img&lt;/code&gt; selector&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;width&lt;/code&gt; and &lt;code&gt;height&lt;/code&gt; attributes in the &lt;code&gt;img&lt;/code&gt; tag, with &lt;code&gt;height: auto&lt;/code&gt; CSS&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Aspect Ratio CSS
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/aspect-ratio" rel="noopener noreferrer"&gt;aspect-ratio&lt;/a&gt; CSS property, as you guessed it, sets the aspect ratio of an element. This means it determines the ratio between the width and height, rather than specifying an image’s exact dimensions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;img&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;aspect-ratio&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;16&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="m"&gt;9&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here we’re assigning CSS to the &lt;code&gt;img&lt;/code&gt; element and using an aspect ratio of 16 units by 9 units.&lt;/p&gt;

&lt;p&gt;What’s cool is that even before loading the image, the browser can now reserve space for it. This means that the layout and content of the page will not shift after the image loads.&lt;/p&gt;

&lt;p&gt;Depending on your design, it also might be worthwhile to set a background color to the &lt;code&gt;img&lt;/code&gt; element so users are given a hint that the image hasn’t loaded yet.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;img&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;aspect-ratio&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;16&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="m"&gt;9&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;gray&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F23h55kk7ei7jw5xdt1qq.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F23h55kk7ei7jw5xdt1qq.gif" alt="Example of layout content not shifting after the image loads" width="183" height="360"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the screen recording above, you’ll notice that there’s initially a gray square in place of the image, which acts as an image placeholder so the content stays in the same place even after the image loads.&lt;/p&gt;

&lt;h3&gt;
  
  
  object-fit: cover
&lt;/h3&gt;

&lt;p&gt;Another recommendation is to also add the &lt;code&gt;object-fit: cover&lt;/code&gt; CSS:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;img&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;aspect-ratio&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;16&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="m"&gt;9&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;object-fit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;cover&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;gray&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/object-fit" rel="noopener noreferrer"&gt;object-fit&lt;/a&gt; property tells the browser what to do if the aspect ratio is off. So instead of forcing a poorly stretched image because the ratio is wrong, the image can instead keep its own aspect ratio, and still fit nicely within the given area.&lt;/p&gt;

&lt;h2&gt;
  
  
  Width and Height Attributes with Height Auto CSS
&lt;/h2&gt;

&lt;p&gt;Another approach that’s worth trying for your use case is to set the &lt;code&gt;width&lt;/code&gt; and &lt;code&gt;height&lt;/code&gt; attribute of the &lt;code&gt;img&lt;/code&gt; element, and also the &lt;code&gt;height&lt;/code&gt; CSS property to &lt;code&gt;auto&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;width=&lt;/span&gt;&lt;span class="s"&gt;"1200"&lt;/span&gt; &lt;span class="na"&gt;height=&lt;/span&gt;&lt;span class="s"&gt;"675"&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"…"&lt;/span&gt; &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;"…"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;img&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;auto&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Similar to the aspect ratio CSS approach, the browser now knows how to calculate how much space the image will take up before it loads.&lt;/p&gt;

&lt;h2&gt;
  
  
  Improve those Lighthouse scores
&lt;/h2&gt;

&lt;p&gt;Which approach you use is up to you, but regardless, if you want an easy way to improve your Lighthouse scores, then ensuring the aspect ratio of your images is a quick win.&lt;/p&gt;

&lt;h2&gt;
  
  
  About me
&lt;/h2&gt;

&lt;p&gt;Hi! I’m &lt;a href="https://twitter.com/adamdbradley" rel="noopener noreferrer"&gt;Adam&lt;/a&gt;, Director of Technology at &lt;a href="https://www.builder.io/" rel="noopener noreferrer"&gt;Builder.io&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We make a way to drag + drop with your components to create pages and other CMS content on your site or app, &lt;a href="https://www.builder.io/blog/headless-cms-workflow" rel="noopener noreferrer"&gt;visually&lt;/a&gt;.&lt;br&gt;
So this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;BuilderComponent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;registerComponent&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="err"&gt;‘&lt;/span&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;io&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;react&lt;/span&gt;&lt;span class="err"&gt;’&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Hero&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Products&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="err"&gt;‘&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;my&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;components&lt;/span&gt;&lt;span class="err"&gt;’&lt;/span&gt;
&lt;span class="c1"&gt;// Dynamically render compositions of your components&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;MyPage&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;json&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;BuilderComponent&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c1"&gt;// Use your components in the drag and drop editor&lt;/span&gt;
&lt;span class="nf"&gt;registerComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Hero&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;registerComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Products&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Gives you this:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fau5t8ti43cci5n6t6cfb.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fau5t8ti43cci5n6t6cfb.gif" alt="Gif of Builder.io" width="760" height="506"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1627708760293605384-250" src="https://platform.twitter.com/embed/Tweet.html?id=1627708760293605384"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1627708760293605384-250');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1627708760293605384&amp;amp;theme=dark"
  }



 &lt;/p&gt;

</description>
      <category>opensource</category>
      <category>security</category>
      <category>webdev</category>
      <category>softwaredevelopment</category>
    </item>
    <item>
      <title>How Partytown's Sync Communication Works</title>
      <dc:creator>Adam Bradley</dc:creator>
      <pubDate>Mon, 11 Oct 2021 17:26:35 +0000</pubDate>
      <link>https://dev.to/adamdbradley/how-partytown-s-sync-communication-works-4244</link>
      <guid>https://dev.to/adamdbradley/how-partytown-s-sync-communication-works-4244</guid>
      <description>&lt;p&gt;Recently we announced the alpha version of Partytown, which is a library that helps relocate third-party scripts into a web worker so that the main thread can be dedicated to just running &lt;em&gt;your&lt;/em&gt; code. For more information on why a website would benefit from this I’d encourage you to also read: &lt;a href="https://dev.to/adamdbradley/introducing-partytown-run-third-party-scripts-from-a-web-worker-2cnp"&gt;Introducing Partytown 🎉: Run Third-Party Scripts From a Web Worker&lt;/a&gt;. This post is more for the curious and talking through “how” Partytown works.&lt;/p&gt;

&lt;h2&gt;
  
  
  Asynchronous postMessage()
&lt;/h2&gt;

&lt;p&gt;Moving long-running and resource intensive tasks into a &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API" rel="noopener noreferrer"&gt;web worker&lt;/a&gt; has been encouraged for many years now. However, the significant constraint is that the communication between the &lt;a href="https://developer.mozilla.org/en-US/docs/Glossary/Main_thread" rel="noopener noreferrer"&gt;main thread&lt;/a&gt;, and web worker, &lt;em&gt;must&lt;/em&gt; be asynchronous. Meaning a message sent by one thread does not wait on the other thread to receive it, nor wait on it to return a value. &lt;/p&gt;

&lt;p&gt;At the core, &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Worker/postMessage" rel="noopener noreferrer"&gt;postMessage()&lt;/a&gt; is basically a fire-and-forget method. This is perfectly fine, and a communication layer can be built around &lt;code&gt;postMessage()&lt;/code&gt; so your code can instead use promises, async/await or even callbacks (all of which are asynchronous). &lt;/p&gt;

&lt;p&gt;An awesome project that can help you easily use Web Workers is &lt;a href="https://github.com/GoogleChromeLabs/comlink" rel="noopener noreferrer"&gt;Comlink&lt;/a&gt;, which &lt;em&gt;“...removes the mental barrier of thinking about postMessage and hides the fact that you are working with workers.”&lt;/em&gt; Comlink is great, but at the time of writing this, you still hit the barrier that the calls between the main thread and web worker must be async.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Third-Party Scripts Can’t Use postMessage()
&lt;/h2&gt;

&lt;p&gt;As I laid out in the &lt;a href="https://dev.to/adamdbradley/introducing-partytown-run-third-party-scripts-from-a-web-worker-2cnp#running-thirdparty-scripts-within-a-web-worker"&gt;first post&lt;/a&gt;, in reality you can’t just refactor third-party scripts. They’re hosted from another domain, controlled by another service, and built to handle countless scenarios so they can be executed by billions of different devices worldwide.&lt;/p&gt;

&lt;p&gt;However, many scripts like Google Analytics, are just collecting information and posting that data to their servers using  &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Navigator/sendBeacon" rel="noopener noreferrer"&gt;navigator.sendBeacon()&lt;/a&gt;. This is the best case scenario because Google Analytics is really just a background task. It can happily run on its own schedule and lazily collect and post data in another thread. &lt;/p&gt;

&lt;p&gt;The problem, however, is there are still calls to blocking APIs that are not available in the web worker. For example &lt;code&gt;document.title&lt;/code&gt; and &lt;code&gt;window.screen.width&lt;/code&gt; are commonly used in scripts, but reading that information is blocking. So while Google Analytics itself is a great candidate to run in the background on another thread, it still requires synchronous communication in order to read values from &lt;code&gt;document&lt;/code&gt; and &lt;code&gt;window&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Since the third-party scripts &lt;em&gt;must&lt;/em&gt; stay as-is, and because web workers &lt;em&gt;must&lt;/em&gt; have an asynchronous communication, we’ve been in this stand-still where the bulk of our performance issues cannot be offloaded into another thread.&lt;/p&gt;

&lt;h2&gt;
  
  
  With this One Weird Synchronous Trick
&lt;/h2&gt;

&lt;p&gt;This is the fun stuff! 🧑‍🔬 Enter the obscure API: &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Synchronous_and_Asynchronous_Requests#synchronous_request" rel="noopener noreferrer"&gt;Synchronous XMLHttpRequest&lt;/a&gt;. In today’s web development it’s a lesser known API for good reason. Basically in the olden days, when we were rocking Adobe Flash, Java applets, and Dreamweaver’ing DHTML, this was pretty common:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;XMLHttpRequest&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;GET&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/data.xml&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// `false` makes the request synchronous&lt;/span&gt;
&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The problem was that in the main thread, this blocking call would lock up the webpage until the response came back. According to &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Synchronous_and_Asynchronous_Requests#synchronous_request" rel="noopener noreferrer"&gt;MDN&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Synchronous XHR is now in deprecation state. The recommendation is that developers move away from the synchronous API and instead use asynchronous requests.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And in today’s web dev landscape, it’s best to instead use the more modern &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API" rel="noopener noreferrer"&gt;fetch()&lt;/a&gt; API. &lt;/p&gt;

&lt;p&gt;However, the &lt;em&gt;web worker’s&lt;/em&gt; ability to execute synchronously is at the core of many tools. Mainly &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/WorkerGlobalScope/importScripts" rel="noopener noreferrer"&gt;importScripts()&lt;/a&gt;, but Synchronous XHR falls in this category too. These synchronous APIs have &lt;em&gt;not&lt;/em&gt; been marked as deprecated from within a &lt;em&gt;web worker&lt;/em&gt;. A quick &lt;a href="https://github.com/search?q=importscripts&amp;amp;type=code" rel="noopener noreferrer"&gt;Github search for importScripts()&lt;/a&gt; shows just how widely used they are. But again, it’s &lt;em&gt;only&lt;/em&gt; available in a web worker!&lt;/p&gt;

&lt;p&gt;So, as it turns out...I guess we &lt;em&gt;can&lt;/em&gt; make the web worker blocking...🧐&lt;/p&gt;

&lt;h2&gt;
  
  
  Intercepting Synchronous Requests
&lt;/h2&gt;

&lt;p&gt;When code is executed from within a worker, and only a worker, we can make synchronous HTTP requests, which effectively block the web worker thread’s execution until the HTTP response comes back. With that power (and with our mad scientist wig on), we have the ability to execute the web worker code as blocking, and then use the HTTP request to asynchronously call &lt;code&gt;postMessage()&lt;/code&gt;. Remember, an HTTP request and response is asynchronous. So while the web worker thread may “think” it’s sync, we can intercept the actual HTTP request and have an asynchronous response.&lt;/p&gt;

&lt;p&gt;This is where the other weird trick comes in. &lt;em&gt;Doctors hate it!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Service workers are able to intercept requests with &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerGlobalScope/onfetch" rel="noopener noreferrer"&gt;onfetch&lt;/a&gt;. This means that the request the web worker makes can also be intercepted and handled by our own code. The requests are not external and do not hit the actual network, but instead are handled locally within the service worker. From within &lt;code&gt;onfetch&lt;/code&gt;, we can then use &lt;code&gt;postMessage()&lt;/code&gt; to do the real async communication. &lt;/p&gt;

&lt;p&gt;A service worker still doesn’t have direct access to the main thread yet. But because we’re now communicating asynchronously, from within the service worker we can then use its &lt;code&gt;postMessage()&lt;/code&gt; to talk to the the main thread, and have the main thread send messages back to the service worker. Then the service worker completes the HTTP response which it already intercepted.&lt;/p&gt;

&lt;p&gt;So we still have the same asynchronous constraint, but with the combination of synchronous XHR, and intercepting requests, we can effectively convert an async call into a blocking one. Next, we make it a bit easier to use by wrapping up all the main thread’s access with &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy" rel="noopener noreferrer"&gt;Javascript Proxies&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Additionally, Partytown should still work for legacy browsers. Part of its initialization is that if the browser doesn’t support service workers, then it basically just runs the third-party scripts the traditional way (what we’re all doing today).&lt;/p&gt;

&lt;h2&gt;
  
  
  What About Atomics?
&lt;/h2&gt;

&lt;p&gt;Awesome, glad you asked. Personally, I’m hopeful to see &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Atomics" rel="noopener noreferrer"&gt;Atomics&lt;/a&gt; as the solution in the long run. Since the &lt;a href="https://github.com/BuilderIO/partytown" rel="noopener noreferrer"&gt;awesome OSS community&lt;/a&gt; has stepped up with some great ideas, we’ve already broken ground on having two builds available: atomics and service workers. When the library runs, it’ll decide which to use depending on the browser’s support.&lt;/p&gt;

&lt;p&gt;Currently, the plan is that the service worker trick will ultimately become the fallback, but there’s much more Atomics research to do. Good news for the future of Atomics is that Safari Tech Preview just enabled SharedArrayBuffer!&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1443650219770617857-311" src="https://platform.twitter.com/embed/Tweet.html?id=1443650219770617857"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1443650219770617857-311');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1443650219770617857&amp;amp;theme=dark"
  }



 &lt;/p&gt;

&lt;h2&gt;
  
  
  What’s Next
&lt;/h2&gt;

&lt;p&gt;Partytown is still in alpha and undergoing many changes on each commit 😬. But we're already actively running it on a few pages within &lt;a href="https://www.builder.io/" rel="noopener noreferrer"&gt;Builder.io&lt;/a&gt; so we can collect more production data. &lt;/p&gt;

&lt;p&gt;Additionally, we’re working with a few ecommerce sites who have significant third-party script usage, and see if we can help improve their performance and usability. We’d love to have you hop in our &lt;a href="https://discord.gg/hbuEtxdEZ3" rel="noopener noreferrer"&gt;Discord channel&lt;/a&gt; and chat ideas, or even help test and file issues in our &lt;a href="https://github.com/Builderio/partytown" rel="noopener noreferrer"&gt;Github repo&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;So please stay tuned as we continue this experiment. In follow up posts we’ll continue to dig deeper into other parts of the library, and as we gather more data we’re hoping to present some good hard numbers showing Partytown’s benefits.&lt;/p&gt;

&lt;p&gt;Party on, Garth!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/Builderio/partytown" rel="noopener noreferrer"&gt;Partytown Repo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://discord.gg/hbuEtxdEZ3" rel="noopener noreferrer"&gt;Discord channel&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/adamdbradley/introducing-partytown-run-third-party-scripts-from-a-web-worker-2cnp"&gt;Introducing Partytown 🎉: Run Third-Party Scripts From a Web Worker&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/mhevery/series/13467"&gt;Qwik&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.builder.io/" rel="noopener noreferrer"&gt;Builder.io&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
    </item>
    <item>
      <title>Introducing Partytown 🎉: Run Third-Party Scripts From a Web Worker</title>
      <dc:creator>Adam Bradley</dc:creator>
      <pubDate>Thu, 23 Sep 2021 15:59:13 +0000</pubDate>
      <link>https://dev.to/adamdbradley/introducing-partytown-run-third-party-scripts-from-a-web-worker-2cnp</link>
      <guid>https://dev.to/adamdbradley/introducing-partytown-run-third-party-scripts-from-a-web-worker-2cnp</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;A fun location for your third-party scripts to hang out&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Performance is always top of mind for any website or web app. It’s of no surprise that a page that loads instantly, has no scroll jank, and responds immediately to any interaction, will provide an all around better user-experience. &lt;/p&gt;

&lt;p&gt;Even with a fast and highly tuned site following all of the best practices, it's all too common for your performance wins to be erased the moment third-party scripts are added. By third-party scripts we mean code that is embedded within your site, but not directly under your control. A few examples include: analytics, ad pixels, A/B testing, trackers, etc.&lt;/p&gt;

&lt;p&gt;When it comes to improving site performance, resources often explain and document tangible improvements with what you can do to &lt;em&gt;your&lt;/em&gt; code, but for the most part our hands are tied when it comes to improving &lt;em&gt;third-party&lt;/em&gt; code. &lt;/p&gt;

&lt;h2&gt;
  
  
  Third-Party Script Performance Issues
&lt;/h2&gt;

&lt;p&gt;The elephant in the room is that third-party scripts are often to blame for eating up a large chunk of the main thread’s precious resources. There’s a few tricks to reduce their upfront damaging effects, like waiting until after the page load to run these scripts. &lt;/p&gt;

&lt;p&gt;But regardless, they’re still running hundreds of kilobytes (and commonly, even a few megabytes) of Javascript on your user’s main thread! And end-users’ mobile devices have less resources than the machines developers are building the sites on! This can drastically affect &lt;a href="https://developers.google.com/web/tools/lighthouse"&gt;Lighthouse scores&lt;/a&gt;, &lt;a href="https://web.dev/vitals/"&gt;Core Web Vitals&lt;/a&gt;, &lt;a href="https://developers.google.com/search/docs/advanced/experience/page-experience"&gt;search rankings&lt;/a&gt;, and even increase bounce rates and reduce user-engagement due to poor user experience.&lt;/p&gt;

&lt;p&gt;All of this has surfaced as we’ve been building out &lt;a href="https://dev.to/mhevery/series/13467"&gt;Qwik&lt;/a&gt; for &lt;a href="https://www.builder.io/"&gt;Builder.io&lt;/a&gt;. The tldr is that we can make interactive sites load immediately with only HTML and CSS, and only pull in the Javascript you need on-demand. But either way, even with the fastest of the fastest frameworks (or no framework at all), third-party scripts continue to drain site performance. So we got to thinking...&lt;/p&gt;

&lt;h2&gt;
  
  
  Running Third-Party Scripts Within a Web Worker
&lt;/h2&gt;

&lt;p&gt;Partytown's philosophy is that the main thread should be dedicated to your code, and any scripts that are not required to be in the &lt;a href="https://developers.google.com/web/fundamentals/performance/critical-rendering-path"&gt;critical path&lt;/a&gt; should be relocated to a &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API"&gt;web worker&lt;/a&gt;. Into a sandboxed location, kinda like...a little town for third-party scripts. Some sort of a...Partytown, if you will…&lt;/p&gt;

&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API"&gt;Web workers&lt;/a&gt; have been a practical solution that can off-load resource intensive tasks off of the &lt;a href="https://developer.mozilla.org/en-US/docs/Glossary/Main_thread"&gt;main thread&lt;/a&gt; for many years now. The challenge, however, is that workers do not have direct access to main thread APIs, such as &lt;code&gt;window&lt;/code&gt;, &lt;code&gt;document&lt;/code&gt;, or &lt;code&gt;localStorage&lt;/code&gt;. A messaging system can be created between the two worlds, but because &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Worker/postMessage"&gt;postMessage&lt;/a&gt; is &lt;a href="https://developer.mozilla.org/en-US/docs/Glossary/Asynchronous"&gt;asynchronous&lt;/a&gt;, DOM operations that third-party scripts are packed full of simply won’t succeed with a traditional messaging system.&lt;/p&gt;

&lt;p&gt;For example, here’s a snippet of code found in &lt;a href="https://support.google.com/tagmanager/answer/6102821?hl=en"&gt;Google Tag Manager&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;w&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clientWidth&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There’s nothing special about this code, actually it’s pretty darn common. But, notice how it has to be synchronous, and there’s three blocking getters:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Get &lt;code&gt;document&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Get &lt;code&gt;body&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Get &lt;code&gt;clientWidth&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If we’re unable to refactor this code to use promises or callbacks instead, then an asynchronous messaging system wouldn’t allow this to “just work.” And I want to emphasize, “unable to refactor this code.” &lt;/p&gt;

&lt;p&gt;The same third-party scripts that are being executed by billions of devices, even as you are reading these lines, cannot just be “refactored.” In a perfect world, I’d message Google and say, “Hey, you know that analytics code that gazillions of dollars are dependent on? Please refactor it entirely. Thank you.” Next, I’d have to DM every single service in the world to refactor their code too. Wish me luck, but results may vary.&lt;/p&gt;

&lt;h2&gt;
  
  
  Take Me To Partytown
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.npmjs.com/package/@builder.io/partytown"&gt;Partytown&lt;/a&gt; is a lazy loaded &lt;code&gt;6kb&lt;/code&gt; library that helps relocate resource intensive scripts into a &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API"&gt;web worker&lt;/a&gt; and off of the &lt;a href="https://developer.mozilla.org/en-US/docs/Glossary/Main_thread"&gt;main thread&lt;/a&gt;. Its goal is to help speed up sites by dedicating the main thread to your code, and offloading third-party scripts to a web worker.&lt;/p&gt;

&lt;p&gt;But, the most important piece it brings to the table is allowing the web worker to &lt;em&gt;synchronously&lt;/em&gt; read from the main thread. If code running within the web worker can call blocking DOM APIs with synchronous return values, then that means we can run, unaltered, third-party scripts in a worker. The third-party code happily executes as intended, but within a different thread as to not take resources away from &lt;em&gt;your&lt;/em&gt; code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sandboxing and Isolation
&lt;/h2&gt;

&lt;p&gt;Third-party scripts are often a black-box with large amounts of Javascript. What's buried within the obfuscated code is difficult to tell. It's minified for good reason, but regardless it becomes very difficult to understand what third-party scripts are executing on &lt;em&gt;your&lt;/em&gt; site and &lt;em&gt;your&lt;/em&gt; users’ devices, and on the same thread/context as your app's code.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.npmjs.com/package/@builder.io/partytown"&gt;Partytown&lt;/a&gt;, on the other hand, is able to sandbox and isolate third-party scripts within a web worker and allow, or deny, access to main thread APIs. This includes cookies, localStorage, userAgent, etc. Because the code &lt;em&gt;must&lt;/em&gt; go through Partytown’s proxy in order to access the main thread, Partytown also has the ability to log every read and write, and even restrict access to certain DOM APIs. &lt;/p&gt;

&lt;p&gt;Essentially, Partytown lets you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Isolate third-party scripts within a sandbox.&lt;/li&gt;
&lt;li&gt;Configure which browser APIs specific scripts can and cannot execute.&lt;/li&gt;
&lt;li&gt;Option to log API calls and arguments in order to give better insight as to what the scripts are doing.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This could be useful for many different use-cases, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Blocking access to &lt;code&gt;document.cookie&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Providing a standard &lt;code&gt;navigator.userAgent&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Not allowing scripts to write to &lt;code&gt;localStorage&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Turning &lt;code&gt;document.write()&lt;/code&gt; into a &lt;code&gt;noop&lt;/code&gt; function&lt;/li&gt;
&lt;li&gt;Block scripts from requesting other scripts&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Current Status and What’s Next
&lt;/h2&gt;

&lt;p&gt;Partytown is still in alpha, it is highly experimental and not ready for production. However, we’ve been actively testing it out on a few pages within our production site on Builder.io, and so far so good. Data is being collected as expected and our analytics look unaffected. Our goal is to collect the data now, so that it can be presented in future posts.&lt;/p&gt;

&lt;p&gt;In the next post, I’ll be focusing on how the synchronous communication channel works and some of its trade-offs. &lt;/p&gt;

&lt;p&gt;Additionally, we’ll show how you can start testing Partytown within a React or &lt;a href="https://nextjs.org/"&gt;Next.js&lt;/a&gt; project, or really any website or web app. Here's a quick example of how Partytown can be used within a Next.js document, but much more to come in follow up posts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Partytown&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;GoogleTagManager&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@builder.io/partytown/react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Document&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Html&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Head&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Main&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;NextScript&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;next/document&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;MyDocument&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;Document&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Html&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Head&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;GoogleTagManager&lt;/span&gt; &lt;span class="na"&gt;containerId&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;GTM-XXXXX&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Partytown&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Head&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Main&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;NextScript&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Html&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you’d like to learn more, or even help test, please come party with us on our &lt;a href="https://discord.gg/hbuEtxdEZ3"&gt;Discord channel&lt;/a&gt;, or ping me at &lt;a href="https://twitter.com/adamdbradley"&gt;@adamdbradley&lt;/a&gt;. I’d love to ensure Partytown can work with your service or use-case, so please don’t hesitate to start a chat.&lt;/p&gt;

&lt;p&gt;I’d also like to thank some awesome people we’ve been lucky enough to bounce ideas off of, and help validate if this could work IRL: &lt;a href="https://twitter.com/addyosmani"&gt;Addy Osmani&lt;/a&gt;, &lt;a href="https://twitter.com/igrigorik"&gt;Ilya Grigorik&lt;/a&gt;, &lt;a href="https://twitter.com/kristoferbaxter"&gt;Kristofer Baxter&lt;/a&gt;, &lt;a href="https://twitter.com/shubhie"&gt;Shubhie Panicker&lt;/a&gt;, &lt;a href="https://twitter.com/zachleat"&gt;Zach Leatherman&lt;/a&gt;, &lt;a href="https://twitter.com/mhevery"&gt;Misko Hevery&lt;/a&gt;, &lt;a href="https://twitter.com/Steve8708"&gt;Steve Sewell&lt;/a&gt; and the entire &lt;a href="https://twitter.com/builderio"&gt;Builder.io team&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Party on, Wayne!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/adamdbradley/how-partytown-s-sync-communication-works-4244"&gt;How Partytown's Sync Communication Works&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Builderio/partytown"&gt;Partytown Repo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://discord.gg/hbuEtxdEZ3"&gt;Discord channel&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/mhevery/series/13467"&gt;Qwik&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.builder.io/"&gt;Builder.io&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>performance</category>
      <category>webdev</category>
      <category>webperf</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
