<?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: Matteo Mazzarolo</title>
    <description>The latest articles on DEV Community by Matteo Mazzarolo (@mmazzarolo).</description>
    <link>https://dev.to/mmazzarolo</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%2F128468%2F38d12479-c308-40f2-a45e-0b637766a5a6.jpeg</url>
      <title>DEV Community: Matteo Mazzarolo</title>
      <link>https://dev.to/mmazzarolo</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mmazzarolo"/>
    <language>en</language>
    <item>
      <title>Track down the JavaScript code responsible for polluting the global scope</title>
      <dc:creator>Matteo Mazzarolo</dc:creator>
      <pubDate>Wed, 23 Feb 2022 13:12:00 +0000</pubDate>
      <link>https://dev.to/mmazzarolo/track-down-the-javascript-code-responsible-for-polluting-the-global-scope-35pm</link>
      <guid>https://dev.to/mmazzarolo/track-down-the-javascript-code-responsible-for-polluting-the-global-scope-35pm</guid>
      <description>&lt;p&gt;Following &lt;a href="https://mmazzarolo.com/blog/2022-02-14-find-what-javascript-variables-are-leaking-into-the-global-scope/" rel="noopener noreferrer"&gt;“Find what JavaScript variables are leaking into the global scope”&lt;/a&gt;, here’s another post to help you solve issues with global scope pollution in JavaScript apps.&lt;/p&gt;

&lt;p&gt;In the previous post, we learned a technique to discover the name of variables being added to the global scope by JavaScript code. Just knowing the global variable names is usually enough to determine 1) if it’s ok or not for the variable to live in the global scope and, if it’s not, 2) what line of JavaScript code is adding it to the global scope.&lt;/p&gt;

&lt;p&gt;Still, sometimes tracking down the JavaScript code responsible for creating a global variable is not that straightforward — for example, when the global variable name is extremely generic (e.g., &lt;code&gt;item&lt;/code&gt;, &lt;code&gt;x&lt;/code&gt;, etc.) or when the code that creates the global is deep into the dependency tree of your JavaScript app.&lt;/p&gt;

&lt;p&gt;So, here’s how to build (from scratch) a JavaScript utility that can help us debugging &lt;strong&gt;where&lt;/strong&gt; the global definitions are happening within out code.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://github.com/brankosekulic/trimHtml/pull/12" rel="noopener noreferrer"&gt;Here’s a real-world example&lt;/a&gt; where this utility has helped me track-down a global variable leak coming from third-party JavaScript code.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Global pollution example
&lt;/h2&gt;

&lt;p&gt;As an example, let’s focus again on the HTML document I shared in the previous post:&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;html&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Hello world!&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://unpkg.com/jquery@3.6.0/dist/jquery.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
      &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;doSomethingTwice&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;myString&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`hello-world-&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
          &lt;span class="c1"&gt;// Let's imagine we're going to do something with myString here...&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="nf"&gt;doSomethingTwice&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The two scripts on the page (&lt;code&gt;jquery.js&lt;/code&gt; and the inline one) add four different global variables: &lt;code&gt;$&lt;/code&gt; and &lt;code&gt;jQuery&lt;/code&gt; from &lt;code&gt;jquery.js&lt;/code&gt;, and &lt;code&gt;doSomethingTwice&lt;/code&gt; and &lt;code&gt;i&lt;/code&gt; from the inline script. Because of how popular jQuery is, the &lt;code&gt;$&lt;/code&gt; and &lt;code&gt;jQuery&lt;/code&gt; global names are pretty easy to associate with the library that creates them (and understand that they’re not global leaks).&lt;br&gt;&lt;br&gt;
The story is different for the two other globals, though:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;doSomethingTwice&lt;/code&gt; is added to the global scope because it’s defined at the root scope (a cleaner approach would be to wrap it in a closure/IIFE). Finding the code responsible for creating this global shouldn’t be difficult with a search &amp;amp; replace in the codebase because &lt;code&gt;doSomethingTwice&lt;/code&gt; is quite a unique name. But what if the global name was more generic (e.g., &lt;code&gt;run&lt;/code&gt;), or if the code was uglified/minified or if it comes from a dependency? That would make it way more difficult to track its declaration down just based on its name.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;i&lt;/code&gt; is (mistakenly) added to the global scope because we’re declaring it with no &lt;code&gt;var&lt;/code&gt;/&lt;code&gt;let&lt;/code&gt;/&lt;code&gt;const&lt;/code&gt; while not being in &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode" rel="noopener noreferrer"&gt;strict mode&lt;/a&gt;. In this small example, it’s rather obvious what line of code declares it. But good luck tracking it down with a search &amp;amp; replace in a bigger app 😅.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So, let’s see how we can make it easy to track down the line of codes responsible for setting global variables in our codebase.&lt;/p&gt;
&lt;h2&gt;
  
  
  Debugging global leaks: inspecting the call stack
&lt;/h2&gt;

&lt;p&gt;Here’s a high-level overview of what we can do to help us track down these pesky global variables:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Take note of the exact global variable name I want to track down (following &lt;a href="https://mmazzarolo.com/blog/2022-02-14-find-what-javascript-variables-are-leaking-into-the-global-scope/" rel="noopener noreferrer"&gt;“Find what JavaScript variables are leaking into the global scope”&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;Proxy the &lt;code&gt;set&lt;/code&gt; instruction of such variable on the &lt;code&gt;window&lt;/code&gt; object to trigger some custom code when the variable is set. The goal of this code is to point out “what” is setting the global variable.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I’ve already covered the first step in the past, so let’s focus on the second one: proxying the &lt;code&gt;window&lt;/code&gt; (or &lt;code&gt;globalThis&lt;/code&gt;) object.&lt;/p&gt;

&lt;p&gt;The idea here is that whenever an assignment like &lt;code&gt;window.i = 1&lt;/code&gt; happens, we want to run some code that tells us the context of where that assignment happened. To be useful, this context should provide us some information about the code that is running it (e.g., tell us the line of code or file where the declaration happened).&lt;br&gt;&lt;br&gt;
Here are a couple of ways to get this info:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;When the global declaration happens, halt the code execution with a &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/debugger" rel="noopener noreferrer"&gt;&lt;code&gt;debugger;&lt;/code&gt;&lt;/a&gt; statement to inspect the context — this is exactly like adding a breakpoint in the script source, and it’s helpful for debugging the scope and closures.&lt;/li&gt;
&lt;li&gt;When the global declaration happens, print the stack trace using &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/console/trace" rel="noopener noreferrer"&gt;&lt;code&gt;console.trace()&lt;/code&gt;&lt;/a&gt;. This is helpful to inspect the stack trace’s code even while the execution is running.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We’ll implement both solutions using an &lt;code&gt;onGlobalDeclaration&lt;/code&gt; function:&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;function&lt;/span&gt; &lt;span class="nf"&gt;onGlobalDeclaration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;globalName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Print the stack trace to the console.&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trace&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="c1"&gt;// Halt the code execution (only if the DevTools are running).&lt;/span&gt;
  &lt;span class="k"&gt;debugger&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// TODO: Code that attaches the onGlobalDeclaration listener.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Debugging global leaks: proxying &lt;code&gt;window&lt;/code&gt; attributes
&lt;/h2&gt;

&lt;p&gt;Now that we can get some contextual information about the stack, how can we attach invoke &lt;code&gt;onGlobalDeclaration&lt;/code&gt; when the global variable is set?&lt;/p&gt;

&lt;p&gt;In the past, I tried a few different options, but to me the one that works better is to instantiate the global variable ourselves as a proxy &lt;strong&gt;before&lt;/strong&gt; it gets set by the rest of our codebase. Basically, &lt;strong&gt;before&lt;/strong&gt; a &lt;code&gt;window.i = 1&lt;/code&gt; statement runs, we want to instantiate &lt;code&gt;window.i&lt;/code&gt; ourselves and override its setter function so that, whenever it’s invoked, we also invoke &lt;code&gt;onGlobalDeclaration&lt;/code&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;function&lt;/span&gt; &lt;span class="nf"&gt;addGlobalToInspect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;globalName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;onGlobalDeclaration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;globalName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Print the stack trace to the console.&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trace&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="c1"&gt;// Halt the code execution (only if the DevTools are running).&lt;/span&gt;
    &lt;span class="k"&gt;debugger&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// Proxy the global variable that we're interested in.&lt;/span&gt;
  &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;defineProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;globalName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;set&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// Invoke onGlobalDeclaration and set the value in a proxy attribute.&lt;/span&gt;
      &lt;span class="nf"&gt;onGlobalDeclaration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;globalName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;` __globals-debugger-proxy-for-&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;globalName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&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;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// When the global is requested, return the proxy attribute value.&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;` __globals-debugger-proxy-for-&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;globalName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;__ `&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;configurable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&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;span class="c1"&gt;// Inspect the strack whenever an "i" variable is added to the global scope.&lt;/span&gt;
&lt;span class="nf"&gt;addGlobalToInspect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;i&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;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note on ES6 proxies&lt;/strong&gt; : ES6 introduced the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy" rel="noopener noreferrer"&gt;&lt;code&gt;Proxy&lt;/code&gt;&lt;/a&gt; object to cover similar use-cases. If you’re not familiar with it, the &lt;code&gt;Proxy&lt;/code&gt; object allows you to create an object that can be used in place of the original object, but which may redefine fundamental Object operations like getting, setting, and defining properties.&lt;br&gt;&lt;br&gt;
Unfortunately, the &lt;code&gt;Proxy&lt;/code&gt; object doesn’t work well for our use case because of how the &lt;code&gt;window&lt;/code&gt; object is implemented at the browser level (with proxies, we’d need to override it with its own proxy instance, which we can’t do), so we need to fallback to the monkey-patching approach.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Nice! Now our code is (kinda) ready to intercept globals declaration. The next step is to ensure we run &lt;code&gt;addGlobalToInspect&lt;/code&gt; &lt;strong&gt;before&lt;/strong&gt; the global declarations statement.&lt;/p&gt;
&lt;h2&gt;
  
  
  Debugging global leaks: integrating the global inspector
&lt;/h2&gt;

&lt;p&gt;We still need to do two things to finalize our debugging flow.&lt;/p&gt;

&lt;p&gt;First of all, we must make sure to run &lt;code&gt;addGlobalToInspect&lt;/code&gt; &lt;strong&gt;before&lt;/strong&gt; setting the global we want to inspect. It’s up to you to decide how and when to do so, but my suggestion is to put the global inspector code in its own .js file (e.g., &lt;code&gt;globals-debugger.js&lt;/code&gt;) and make sure to load it before all other scripts:&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;html&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Hello world!&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
    &lt;span class="c"&gt;&amp;lt;!--- 
    Make sure to load globals-debugger.js first. 
    It might be wise to load it conditionally depending 
    on the environment (e.g., do not load it in production).
    —--&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"./globals-debugger.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://unpkg.com/jquery@3.6.0/dist/jquery.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; 
    &lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
      &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;doSomethingTwice&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;myString&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`hello-world-&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
          &lt;span class="c1"&gt;// Let's imagine we're going to do something with myString here...&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="nf"&gt;doSomethingTwice&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Then, it would be nice to pick the globals to inspect dynamically instead of hardcoding them in the code like we’re doing now (as we’re doing with &lt;code&gt;addGlobalToInspect("i")&lt;/code&gt;).&lt;br&gt;&lt;br&gt;
Since our script runs ASAP, I think the easiest way to pass the global names as parameters is by appending them to URL as query parameters.&lt;br&gt;&lt;br&gt;
For example, we can change our script so that when the page is loaded with &lt;code&gt;?globalsToInspect=i,jQuery&lt;/code&gt; in the URL, it will automatically start inspecting for the &lt;code&gt;i&lt;/code&gt; and &lt;code&gt;jQuery&lt;/code&gt; globals:&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="c1"&gt;// Grab the global to inspect from the URL's "globalsToInspect" query parameter.&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;parsedUrl&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;URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;href&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;parsedUrl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;searchParams&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;globalsToInspect&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Boolean&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;globalToInspect&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;addGlobalToInspect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;globalToInspect&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Complete solution: &lt;code&gt;globals-debugger.js&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Before finally trying the globals debugger, here’s the complete code (with comments and a couple of additional safety checks):&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;h2&gt;
  
  
  &lt;code&gt;globals-debugger.js&lt;/code&gt; usage example
&lt;/h2&gt;

&lt;p&gt;Finally, here’s an example of using what we just built to track down the &lt;code&gt;i&lt;/code&gt; global creation.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; This is just a simplified use-case to show you the &lt;code&gt;globals-debugger&lt;/code&gt; use-flow. In a more realistic scenario you would probably use it to track down globals added in bigger codebases or from third-party libraries deep down the dependencies tree.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Opening the HTML page above with the &lt;code&gt;?globalsToInspect=i&lt;/code&gt; query parameter will immediately pause the code execution when the &lt;code&gt;i&lt;/code&gt; variable is being set (notice that the &lt;code&gt;globalName&lt;/code&gt; variable in the current closure is &lt;code&gt;i&lt;/code&gt; in the right panel):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmmazzarolo.com%2Fstatic%2F2239b19548f18adda13be1ce3bc63e9b%2F1e043%2Fi-1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmmazzarolo.com%2Fstatic%2F2239b19548f18adda13be1ce3bc63e9b%2F1e043%2Fi-1.png" title="i 1" alt="i 1"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since the &lt;code&gt;debugger;&lt;/code&gt; statement is in our own code, we need to step out of the current function (Shift + F11), to land on the exact line of code that is setting the &lt;code&gt;i&lt;/code&gt; variable:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmmazzarolo.com%2Fstatic%2Ffb763f6857257582890d2d3416f96fd3%2F1e043%2Fi-2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmmazzarolo.com%2Fstatic%2Ffb763f6857257582890d2d3416f96fd3%2F1e043%2Fi-2.png" title="i 2" alt="i 2"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Last but not least, if we check the DevTools console we’ll see the logged stack trace, which is helpful to inspect the stack even while the script is running. Also, we can validate that, even if proxied, the global variables are still working correctly:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmmazzarolo.com%2Fstatic%2Fbdbc676352e66d7e5268cc52005c3bd2%2F1e043%2Fconsole.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmmazzarolo.com%2Fstatic%2Fbdbc676352e66d7e5268cc52005c3bd2%2F1e043%2Fconsole.png" title="console" alt="console"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>tutorial</category>
      <category>frontend</category>
    </item>
    <item>
      <title>Find what JavaScript variables are leaking into the global scope</title>
      <dc:creator>Matteo Mazzarolo</dc:creator>
      <pubDate>Tue, 22 Feb 2022 15:13:00 +0000</pubDate>
      <link>https://dev.to/mmazzarolo/find-what-javascript-variables-are-leaking-into-the-global-scope-1aik</link>
      <guid>https://dev.to/mmazzarolo/find-what-javascript-variables-are-leaking-into-the-global-scope-1aik</guid>
      <description>&lt;p&gt;Detecting global variable leaks can be helpful to debug your apps and avoid collisions in the global scope. The more a web app grows, the more having a good understanding of what’s happening in the global scope becomes important (e.g., to ensure multiple libraries — &lt;a href="https://micro-frontends.org/" rel="noopener noreferrer"&gt;or even multiple apps!&lt;/a&gt; — can coexist on the page without collisions).&lt;/p&gt;

&lt;p&gt;In this post, I’ll show you how to find variables that have been added or leaked into the global scope at runtime in web apps (thanks to &lt;a href="https://twitter.com/DevelopSean" rel="noopener noreferrer"&gt;@DevelopSean&lt;/a&gt; for introducing me to this trick at InVision).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Disclaimer&lt;/strong&gt; : in this post, I’ll reference global scope using the &lt;code&gt;window&lt;/code&gt; property. Please notice that, in most cases, the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/globalThis" rel="noopener noreferrer"&gt;&lt;code&gt;globalThis&lt;/code&gt;&lt;/a&gt; property would be a better candidate to access the global object since it works in different JavaScript environments. That said, this post is specific to web (non-worker) contexts, so I think using the &lt;code&gt;window&lt;/code&gt; term here makes it easier to follow.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;Let’s say you want to check what global variables are being added to the &lt;code&gt;window&lt;/code&gt; object on this page (with some code that looks bad on purpose):&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;html&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Hello world!&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://unpkg.com/jquery@3.6.0/dist/jquery.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
      &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;doSomethingTwice&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;myString&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`hello-world-&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
          &lt;span class="c1"&gt;// Let's imagine we're going to do something with myString here...&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="nf"&gt;doSomethingTwice&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Typically, you’d probably open the DevTools console and inspect the &lt;code&gt;window&lt;/code&gt; object looking for suspicious variables.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmmazzarolo.com%2Fstatic%2Ffab9d11e2a16ef47f2a5d12e123dbf25%2F1e043%2Ftoo-many-globals.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmmazzarolo.com%2Fstatic%2Ffab9d11e2a16ef47f2a5d12e123dbf25%2F1e043%2Ftoo-many-globals.png" title="too many globals" alt="too many globals"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This approach can work, but… it’s a lot of work. The browser and the JavaScript engine themselves add a bunch of globals on the &lt;code&gt;window&lt;/code&gt; object (e.g., JavaScript APIs such as &lt;code&gt;localStorage&lt;/code&gt;, etc.), so finding globals introduced by our code is like looking for a needle in a haystack.&lt;/p&gt;

&lt;p&gt;One possible way to get around this issue is to grab a list of all the default globals and filter them out from the &lt;code&gt;window&lt;/code&gt; object by running a similar snippet in the DevTools console:&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;const&lt;/span&gt; &lt;span class="nx"&gt;browserGlobals&lt;/span&gt; &lt;span class="o"&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;window&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;self&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;document&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;name&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;location&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;customElements&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;history&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;locationbar&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;menubar&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;personalbar&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;scrollbars&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;statusbar&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;toolbar&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;status&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;closed&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;frames&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;length&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;top&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...];&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;runtimeGlobals&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isFromBrowser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;browserGlobals&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;isFromBrowser&lt;/span&gt;&lt;span class="p"&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="nf"&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;Runtime globals&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;runtimeGlobals&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Doing so should work, but it leaves two open questions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How do you get the &lt;code&gt;browserGlobals&lt;/code&gt; variables?&lt;/li&gt;
&lt;li&gt;Between cross-browser differences and JavaScript API updates, maintaining the &lt;code&gt;browserGlobals&lt;/code&gt; list can quickly get hairy. Can we make it better?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To answer both questions, we can generate the &lt;code&gt;browserGlobals&lt;/code&gt; list programmatically by populating it with the globals of a pristine &lt;code&gt;window&lt;/code&gt; object.&lt;br&gt;&lt;br&gt;
There are a couple of ways to do it, but to me, the cleanest approach is to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a disposable iframe pointing it at &lt;code&gt;about:blank&lt;/code&gt; (to ensure the &lt;code&gt;window&lt;/code&gt; object is in a clean state).&lt;/li&gt;
&lt;li&gt;Inspect the iframe &lt;code&gt;window&lt;/code&gt; object and store its global variable names.&lt;/li&gt;
&lt;li&gt;Remove the iframe.
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Grab browser's default global variables.&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;iframe&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;iframe&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;iframe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;about:blank&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&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="nf"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;iframe&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;browserGlobals&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;iframe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;contentWindow&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&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="nf"&gt;removeChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;iframe&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Get the global variables added at runtime by filtering out the browser's&lt;/span&gt;
  &lt;span class="c1"&gt;// default global variables from the current window object.&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;runtimeGlobals&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isFromBrowser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;browserGlobals&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;isFromBrowser&lt;/span&gt;&lt;span class="p"&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="nf"&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;Runtime globals&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;runtimeGlobals&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;Run the snippet above in the console, and you’ll finally see a clean list with of the runtime variables 👍&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmmazzarolo.com%2Fstatic%2Fd0118bb0ac9c42fd0afd75f74cde0cc1%2F1e043%2Fruntime-globals-snippet.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmmazzarolo.com%2Fstatic%2Fd0118bb0ac9c42fd0afd75f74cde0cc1%2F1e043%2Fruntime-globals-snippet.png" title="runtime globals snippet" alt="runtime globals snippet"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For a more complex version of the script, I created this Gist:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;p&gt;A couple of final notes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;This utility can easily run in a Continuous Integration context (e.g., in E2E tests using Cypress) to provide automated feedback.&lt;/li&gt;
&lt;li&gt;I recommend running this utility in browser tabs with no extensions: most browser extensions inject global variables in the &lt;code&gt;window&lt;/code&gt; object, adding noise to the result (e.g., &lt;code&gt;__REACT_DEVTOOLS_BROWSER_THEME__&lt;/code&gt;, etc. from the React DevTools extension).&lt;/li&gt;
&lt;li&gt;To avoid repeatedly copy/pasting the global checker code in your DevTools console, you can create a &lt;a href="https://developer.chrome.com/docs/devtools/javascript/snippets/" rel="noopener noreferrer"&gt;JavaScript snippet&lt;/a&gt; instead.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>tutorial</category>
      <category>frontend</category>
    </item>
    <item>
      <title>Creating and deploying a tiny proxy server on Vercel in 10 minutes</title>
      <dc:creator>Matteo Mazzarolo</dc:creator>
      <pubDate>Tue, 22 Feb 2022 14:48:00 +0000</pubDate>
      <link>https://dev.to/mmazzarolo/creating-and-deploying-a-tiny-proxy-server-on-vercel-in-10-minutes-3am1</link>
      <guid>https://dev.to/mmazzarolo/creating-and-deploying-a-tiny-proxy-server-on-vercel-in-10-minutes-3am1</guid>
      <description>&lt;p&gt;I recently created &lt;a href="https://github.com/mmazzarolo/pinboard-api-proxy"&gt;a tiny proxy server to edit responses of a public API on the fly&lt;/a&gt;, and I was impressed by how easy it is to build and deploy such things on &lt;a href="https://vercel.com/"&gt;Vercel&lt;/a&gt;.&lt;br&gt;&lt;br&gt;
In my case, the goal was to allow all origins to fetch the &lt;a href="https://pinboard.in/howto/#api"&gt;Pinboard API&lt;/a&gt; by adding an &lt;code&gt;"Access-Control-Allow-Origin": "*"&lt;/code&gt; header to the API response, but there are plenty of other cases where a proxy server may come in handy.&lt;/p&gt;

&lt;p&gt;So, here’s how you can create and deploy a tiny but flexible &lt;a href="https://nodejs.org/"&gt;Node.js&lt;/a&gt; proxy server on Vercel in 10 minutes.&lt;/p&gt;
&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;We’ll create a Node.js serverless function that uses &lt;a href="https://github.com/chimurai/http-proxy-middleware"&gt;&lt;code&gt;http-proxy-middleware&lt;/code&gt;&lt;/a&gt; to proxy any &lt;code&gt;/api&lt;/code&gt; request to (e.g.) &lt;code&gt;https://example.org&lt;/code&gt;. Within the serverless function code, we can intercept requests/responses and manipulate them on the fly.&lt;br&gt;&lt;br&gt;
The serverless function will be deployed on Vercel.&lt;/p&gt;
&lt;h2&gt;
  
  
  Project setup
&lt;/h2&gt;

&lt;p&gt;Create a project directory, &lt;code&gt;cd&lt;/code&gt; into it, and initialize an npm project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;my-proxy &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;my-proxy
npm init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Install &lt;a href="https://github.com/vercel/vercel"&gt;&lt;code&gt;vercel&lt;/code&gt;&lt;/a&gt; as a dependency:&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="nx"&gt;npm&lt;/span&gt; &lt;span class="nx"&gt;install&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;D&lt;/span&gt; &lt;span class="nx"&gt;vercel&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Update the &lt;code&gt;start&lt;/code&gt; script of your &lt;code&gt;package.json&lt;/code&gt; to &lt;code&gt;"start": "vercel dev"&lt;/code&gt; in order to run your serverless function locally:&lt;/p&gt;

&lt;p&gt;my-proxy/package.json&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"my-proxy"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.0.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
     &lt;/span&gt;&lt;span class="nl"&gt;"start"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"vercel dev"&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create an &lt;code&gt;api&lt;/code&gt; directory and an &lt;code&gt;index.js&lt;/code&gt; file inside of it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;api &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;touch &lt;/span&gt;api/index.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Vercel serverless functions use a &lt;a href="https://vercel.com/docs/concepts/functions/serverless-functions"&gt;file-system-based convention&lt;/a&gt;. So the &lt;code&gt;api/index.js&lt;/code&gt; file you just created will automatically handle all requests of the &lt;code&gt;/api&lt;/code&gt; endpoint:&lt;/p&gt;

&lt;p&gt;my-proxy/api/index.js&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="c1"&gt;// In Vercel, any file inside the "api" directory is exposed on an "/api" endpoint.&lt;/span&gt;

&lt;span class="c1"&gt;// For an API route to work, you need to export a function as default (a.k.a request handler),&lt;/span&gt;
&lt;span class="c1"&gt;// which then receives the following parameters:&lt;/span&gt;
&lt;span class="c1"&gt;// - req: The request object.&lt;/span&gt;
&lt;span class="c1"&gt;// - res: The response object.&lt;/span&gt;
&lt;span class="c1"&gt;// See https://vercel.com/docs/serverless-functions/supported-languages#node.js for details.&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Hello world!`&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;By default, on Vercel, the &lt;code&gt;/api/index.js&lt;/code&gt; file would &lt;strong&gt;strictly match only requests made to the &lt;code&gt;/api&lt;/code&gt; endpoint&lt;/strong&gt; , ignoring sub-paths like &lt;code&gt;/api/hello&lt;/code&gt;.&lt;br&gt;&lt;br&gt;
To make &lt;code&gt;/api/index.js&lt;/code&gt; handle the entire path, we can configure a &lt;a href="https://vercel.com/docs/cli#project-configuration/rewrites"&gt;Vercel rewrite&lt;/a&gt; to redirect all &lt;code&gt;/api/*&lt;/code&gt; calls to the &lt;code&gt;/api/index.js&lt;/code&gt; file (by specifying the rewrite rule in a &lt;code&gt;vercel.json&lt;/code&gt; file at the root of the project):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;touch &lt;/span&gt;vercel.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;my-proxy/vercel.json&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"rewrites"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/api/(.*)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"destination"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/api"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should now be able to deploy your code to Vercel (of course, we haven’t added any “real” logic in &lt;code&gt;api/index.js&lt;/code&gt;, so it won’t do anything for now).&lt;br&gt;&lt;br&gt;
My go-to approach on these occasions is to &lt;a href="https://vercel.com/docs/git/vercel-for-github"&gt;create a GitHub repo and connect it to Vercel&lt;/a&gt; to automatically deploy the project on each commit. But you can also follow the automated setup by running &lt;code&gt;npm start&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Proxy logic setup
&lt;/h2&gt;

&lt;p&gt;Install &lt;a href="https://github.com/chimurai/http-proxy-middleware"&gt;&lt;code&gt;http-proxy-middleware&lt;/code&gt;&lt;/a&gt;, an easy-to-use proxy middleware compatible with Vercel:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i http-proxy-middleware
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In &lt;code&gt;/api/index.js&lt;/code&gt;, use &lt;code&gt;http-proxy-middleware&lt;/code&gt; to create a new proxy and expose it on the route handler:&lt;/p&gt;

&lt;p&gt;my-proxy/api/index.js&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="c1"&gt;// Create a proxy to redirect requests of the "/api/*" path to "https://example.org".&lt;/span&gt;
&lt;span class="c1"&gt;//&lt;/span&gt;
&lt;span class="c1"&gt;// Examples:&lt;/span&gt;
&lt;span class="c1"&gt;// GET /api/hello → GET https://example.org/hello&lt;/span&gt;
&lt;span class="c1"&gt;// POST /api/test?color=red → POST https://example.org/test?color=red&lt;/span&gt;
&lt;span class="c1"&gt;//&lt;/span&gt;
&lt;span class="c1"&gt;// Additionally, the proxy will:&lt;/span&gt;
&lt;span class="c1"&gt;// - Add an "x-added" header&lt;/span&gt;
&lt;span class="c1"&gt;// - Remove the "x-removed" header&lt;/span&gt;
&lt;span class="c1"&gt;// From the proxied response.&lt;/span&gt;
&lt;span class="c1"&gt;//&lt;/span&gt;
&lt;span class="c1"&gt;// You can/should update the proxy to suit your needs.&lt;/span&gt;
&lt;span class="c1"&gt;// See https://github.com/chimurai/http-proxy-middleware for more details.&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createProxyMiddleware&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http-proxy-middleware&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;apiProxy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;createProxyMiddleware&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://example.org&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;changeOrigin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;pathRewrite&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;^/api&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="c1"&gt;// strip "/api" from the URL&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="nx"&gt;onProxyRes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;proxyRes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;proxyRes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;x-added&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;foobar&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// add new header to response&lt;/span&gt;
    &lt;span class="k"&gt;delete&lt;/span&gt; &lt;span class="nx"&gt;proxyRes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;x-removed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt; &lt;span class="c1"&gt;// remove header from response&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Expose the proxy on the "/api/*" endpoint.&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;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&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="nx"&gt;apiProxy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&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;Et voilà!&lt;/p&gt;

&lt;p&gt;By deploying your code (or running it locally with &lt;code&gt;npm start&lt;/code&gt;) any call made to the &lt;code&gt;/api&lt;/code&gt; endpoint will be proxied to &lt;code&gt;https://example.org&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Check &lt;a href="https://github.com/chimurai/http-proxy-middleware"&gt;the documentation of the &lt;code&gt;http-proxy-middleware&lt;/code&gt; library&lt;/a&gt; (and of the &lt;a href="https://github.com/http-party/node-http-proxy"&gt;&lt;code&gt;node-http-proxy&lt;/code&gt; library&lt;/a&gt;, used under-the-hood) to learn how you can manipulate the proxied request &amp;amp; response.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>nextjs</category>
      <category>tutorial</category>
      <category>node</category>
    </item>
    <item>
      <title>Speed up your TypeScript monorepo with esbuild</title>
      <dc:creator>Matteo Mazzarolo</dc:creator>
      <pubDate>Sat, 06 Nov 2021 00:00:00 +0000</pubDate>
      <link>https://dev.to/mmazzarolo/speed-up-your-typescript-monorepo-with-esbuild-3n85</link>
      <guid>https://dev.to/mmazzarolo/speed-up-your-typescript-monorepo-with-esbuild-3n85</guid>
      <description>&lt;p&gt;&lt;a href="https://www.typescriptlang.org/"&gt;TypeScript&lt;/a&gt; monorepos are a great way to organize medium-to-big size projects. TypeScript improves the developer experience by adding type-checking and a deep IDE integration. And using a monorepo helps in scaling your project(s).&lt;/p&gt;

&lt;p&gt;Compared to plain JavaScript, however, TypeScript adds an additional compilation layer to your project, which may slow down the developer experience. While the native TypeScript compiler is not &lt;em&gt;that&lt;/em&gt; slow (IMHO), it’s still something you need to take into account if you’re planning to build a large codebase. But what if there was a way to speed up the TypeScript compilation by using a different compiler?&lt;/p&gt;

&lt;p&gt;Enter &lt;a href="https://esbuild.github.io/"&gt;esbuild&lt;/a&gt;: a fast JavaScript bundler that claims to be &amp;gt;10x faster than similar projects (webpack, rollup + terser, parcel 2). I’ve been using esbuilt for a couple of TypeScript projects and have been surprised by how well it performs.&lt;/p&gt;

&lt;h2&gt;
  
  
  🍊 Tangerine monorepo
&lt;/h2&gt;

&lt;p&gt;While learning esbuild, I haven’t found many examples of how to integrate it within TypeScript monorepos. So I created my own template: &lt;a href="https://github.com/mmazzarolo/tangerine-monorepo"&gt;🍊 tangerine-monorepo&lt;/a&gt;, a “minimal” TypeScript-based Node.js monorepo setup fully powered by esbuild.&lt;/p&gt;

&lt;h3&gt;
  
  
  Features
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Uses &lt;a href="https://www.typescriptlang.org/"&gt;TypeScript&lt;/a&gt; to write code, tests, and scripts.&lt;/li&gt;
&lt;li&gt;Uses &lt;a href="https://esbuild.github.io/"&gt;esbuild&lt;/a&gt; to compile your TypeScript codebase, tests, and scripts.&lt;/li&gt;
&lt;li&gt;Uses &lt;a href="https://www.typescriptlang.org/docs/handbook/compiler-options.html"&gt;tsc CLI&lt;/a&gt; to type-check the codebase without emitting the compiled files (since they’re handled by esbuild). No need to keep &lt;a href="https://www.typescriptlang.org/docs/handbook/project-references.html"&gt;TypeScript’s Project References&lt;/a&gt; up-to-date.&lt;/li&gt;
&lt;li&gt;Uses &lt;a href="https://github.com/folke/esbuild-runner"&gt;esbuild-runner&lt;/a&gt; to run scripts on the fly.&lt;/li&gt;
&lt;li&gt;Uses &lt;a href="https://yarnpkg.com/features/workspaces"&gt;Yarn workspaces&lt;/a&gt; to make it easy to work within the monorepo.&lt;/li&gt;
&lt;li&gt;Uses &lt;a href="https://github.com/folke/ultra-runner"&gt;Ultra runner&lt;/a&gt; to run scripts from the project root.&lt;/li&gt;
&lt;li&gt;Uses a shareable ESLint config and Jest config(./packages/jest-config) to provide an extensible linting and testing setup.&lt;/li&gt;
&lt;li&gt;Uses esbuild + &lt;a href="https://github.com/remy/nodemon"&gt;nodemon&lt;/a&gt; to reload the server in development mode (even when workspace dependencies are changed).&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Workspaces
&lt;/h3&gt;

&lt;p&gt;Tangerine monorepo includes five workspaces:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;packages/is-even&lt;/code&gt;: The simplest workspace — it doesn’t depend on any other worskpace. It’s a Node.js module that exposes an &lt;code&gt;isEven&lt;/code&gt; function that tells if the input number is even. It includes a CLI script that invokes the function from your terminal, and a test file, both written in TypeScript. The CLI script runs using esbuild-runner, which uses esbuild to compile it on the fly.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;package/is-odd&lt;/code&gt;: Depends on &lt;code&gt;packages/is-even&lt;/code&gt;. It’s a Node.js module that exposes an &lt;code&gt;isOdd&lt;/code&gt; function that tells if the input number is odd (by invoking &lt;code&gt;isEven&lt;/code&gt; and checking if it’s false). It includes a CLI script and a test file.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;package/server&lt;/code&gt;: Depends on both &lt;code&gt;packages/is-odd&lt;/code&gt; and &lt;code&gt;packages/is-even&lt;/code&gt;. It’s a Node.js Express server that exposes two routes that invoke &lt;code&gt;isEven&lt;/code&gt; and &lt;code&gt;isOdd&lt;/code&gt;. It uses nodemon to reload the server in development mode.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;packages/jest-config&lt;/code&gt;: Shared Jest config that uses esbuild to compile your tests and your codebase.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;packages/eslint-config&lt;/code&gt;: Shared ESLint config.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All the workspaces use esbuild to compile the TypeScript codebase. Be it for building, testing, or running CLI scripts, the compilation is instantaneous compared to the native TypeScript compiler (you can quickly test the difference by temporarily swapping esbuild with tsc).&lt;/p&gt;

&lt;p&gt;The tsc CLI is used only to type-check the codebase (without emitting the compiled files — since they’re handled by esbuild). I expect people usually use the IDE integration to type-check the code anyway and explicitly invoke the tsc CLI only in specific use cases (such as pre-commit hooks).&lt;/p&gt;

&lt;p&gt;Each workspace’s package.json is pointing the &lt;code&gt;main&lt;/code&gt; and &lt;code&gt;types&lt;/code&gt; entry to &lt;code&gt;src/index.ts&lt;/code&gt;. Which might look strange at first, given that it’s uncompiled code… see &lt;a href="https://turborepo.com/posts/you-might-not-need-typescript-project-references"&gt;“You might not need TypeScript project references” on the Turborepo blog&lt;/a&gt; for an explanation. This pattern has been working fine for my use cases so far (especially while using esbuild). Still, you might want to update these entries to suit your needs (e.g., when shipping packages to npm).&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>monorepo</category>
      <category>webdev</category>
      <category>node</category>
    </item>
    <item>
      <title>Chrome does some weird stuff if you toggle a stylesheet on and off</title>
      <dc:creator>Matteo Mazzarolo</dc:creator>
      <pubDate>Thu, 21 Oct 2021 11:52:00 +0000</pubDate>
      <link>https://dev.to/mmazzarolo/chrome-does-some-weird-stuff-if-you-toggle-a-stylesheet-on-and-off-2d4o</link>
      <guid>https://dev.to/mmazzarolo/chrome-does-some-weird-stuff-if-you-toggle-a-stylesheet-on-and-off-2d4o</guid>
      <description>&lt;p&gt;Last week I spent more time than I expected debugging a Chrome-specific issue: when toggling the visibility of a view of one of our web apps, the view was flashing with unstyled content before becoming visible.&lt;/p&gt;

&lt;p&gt;The flow that was causing the issue is the following:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;At some point, during the user session, we hide a view and disable the stylesheet (&lt;code&gt;&amp;lt;link href="stylesheet"&amp;gt;&lt;/code&gt;) associated with it (it doesn’t really matter &lt;strong&gt;why&lt;/strong&gt; we’re disabling it).&lt;/li&gt;
&lt;li&gt;Later on, when needed, we re-enable the stylesheet.&lt;/li&gt;
&lt;li&gt;Immediately after re-enabling the stylesheet, we show the view (e.g., by changing its visibility from &lt;code&gt;display: none&lt;/code&gt; to &lt;code&gt;display: block&lt;/code&gt;).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To toggle the stylesheet on and off, we are using the &lt;code&gt;disabled&lt;/code&gt; property of the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/StyleSheet"&gt;StyleSheet interface&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;At this stage, the stylesheet had already been loaded once (before step 1), so expected that re-enabling it (in step 2) would have applied its styles immediately.&lt;/p&gt;

&lt;p&gt;Unfortunately, that’s not what happens in Chrome.&lt;br&gt;&lt;br&gt;
When you re-enable a stylesheet, Chrome (v94.0.4606.71) &lt;strong&gt;sometimes&lt;/strong&gt; tries to fetch it:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--z7IWioS3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://mmazzarolo.com/static/7013393d25c164102b7832d3d20cc990/1e043/devtools.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--z7IWioS3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://mmazzarolo.com/static/7013393d25c164102b7832d3d20cc990/1e043/devtools.png" alt="devtools" title="devtools"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The fetch results in loading the stylesheet from the cache. Still, the browser runs this flow asynchronously, causing a quick flash on unstyled content until the stylesheet is fully loaded:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--fWbhugUq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://mmazzarolo.com/34ce67587fb6a0915b21b45523236747/example-flash.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--fWbhugUq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://mmazzarolo.com/34ce67587fb6a0915b21b45523236747/example-flash.gif" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can reproduce this issue with the following HTML code:&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;html&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt;
      &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt;
      &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://unpkg.com/purecss@2.0.6/build/pure-min.css"&lt;/span&gt;
      &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"purecss"&lt;/span&gt;
    &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"btn"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Toggle CSS&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h1&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Hello world&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;purecss&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;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#purecss&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;btn&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;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#btn&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;text&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;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;btn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;click&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&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="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;purecss&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;disabled&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;purecss&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;disabled&lt;/span&gt; &lt;span class="o"&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;// At this point, the stylesheet on the page should reflect the state&lt;/span&gt;
        &lt;span class="c1"&gt;// set in the previous line — but in Chrome, sometimes it wont.&lt;/span&gt;
        &lt;span class="c1"&gt;// To reproduce the issue consistently, tick "Disable cache" in&lt;/span&gt;
        &lt;span class="c1"&gt;// the network panel.&lt;/span&gt;
        &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;display&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;block&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;debugger&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;display&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;none&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;purecss&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;disabled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&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;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you run your code on Chrome, sometimes you’ll notice that the breakpoint will stop in a state where the stylesheet is not fully loaded.&lt;br&gt;&lt;br&gt;
For example, in the screenshot below, Firefox and Chrome are paused on the same breakpoint. As you can see, in Chrome there’s a pending network request for the stylesheet, and the style has not been applied yet (see the text font style).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--mb_YZ6vQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://mmazzarolo.com/static/5a40d763eeaf7a433f25dfd46b3f044b/1e043/firefox-vs-chrome.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--mb_YZ6vQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://mmazzarolo.com/static/5a40d763eeaf7a433f25dfd46b3f044b/1e043/firefox-vs-chrome.png" alt="firefox vs chrome" title="firefox vs chrome"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I don’t know if this issue is a Chrome bug or not, but I have an idea on why it might be happening.&lt;br&gt;&lt;br&gt;
When you add a &lt;code&gt;disabled&lt;/code&gt; stylesheet to the page (with &lt;code&gt;&amp;lt;link href="stylesheet" disabled&amp;gt;&lt;/code&gt;) and then enable it a runtime dynamically, browsers load the stylesheet on-demand.&lt;br&gt;&lt;br&gt;
My guess is that sometimes Chrome tries to load the stylesheet on-demand even if it has already been loaded before.&lt;/p&gt;

&lt;p&gt;To solve this issue, we must wait for the stylesheet to be fully loaded before showing the view.&lt;br&gt;&lt;br&gt;
Unfortunately, toggling a stylesheet on and off doesn’t trigger its &lt;code&gt;onload&lt;/code&gt; event.&lt;br&gt;&lt;br&gt;
As a (less elegant) alternative, we can use the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Document/styleSheets"&gt;&lt;code&gt;document.styleSheets&lt;/code&gt;&lt;/a&gt; API. This API is a read-only property that returns the list of stylesheets explicitly linked into or embedded in the document. In Chrome, we can expect to find our re-enabled stylesheet in this list only after it has been fully loaded.&lt;/p&gt;

&lt;p&gt;So, we can update our flow this way:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;At some point, during the user session, we hide a view and disable the stylesheet (&lt;code&gt;&amp;lt;link href="stylesheet"&amp;gt;&lt;/code&gt;) associated with it (it doesn’t really matter &lt;strong&gt;why&lt;/strong&gt; we’re disabling it).&lt;/li&gt;
&lt;li&gt;Later on, when needed, we re-enable the stylesheet.&lt;/li&gt;
&lt;li&gt;Wait for the stylesheet to be available in &lt;code&gt;document.styleSheet&lt;/code&gt; (using a quick-looped &lt;code&gt;setInterval&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Immediately after re-enabling the stylesheet, we show the view (e.g., by changing its visibility from &lt;code&gt;display: none&lt;/code&gt; to &lt;code&gt;display: block&lt;/code&gt;).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Example of the solution:&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;html&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt;
      &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt;
      &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://unpkg.com/purecss@2.0.6/build/pure-min.css"&lt;/span&gt;
      &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"purecss"&lt;/span&gt;
    &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"btn"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Toggle CSS&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h1&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Hello world&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;purecss&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;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#purecss&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;btn&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;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#btn&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;text&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;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;isStylesheetLoaded&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="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;styleSheets&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;stylesheet&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;stylesheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;href&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;purecss&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;href&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Wait for the stylesheet to be loaded&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;waitForStylesheet&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isStylesheetLoaded&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// In non-Chromium browsers the stylesheet will immediately be loaded&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;}&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;intervalMs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;intervalId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;setInterval&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isStylesheetLoaded&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;clearInterval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;intervalId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
          &lt;span class="p"&gt;}&lt;/span&gt;
          &lt;span class="c1"&gt;// Handle max retries/timeout if needed.&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;span class="nx"&gt;btn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;click&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;purecss&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;disabled&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;purecss&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;disabled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;waitForStylesheet&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// 👈👈👈&lt;/span&gt;
        &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;display&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;block&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;debugger&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;display&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;none&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;purecss&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;disabled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&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;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>web</category>
      <category>css</category>
      <category>software</category>
      <category>google</category>
    </item>
    <item>
      <title>Running React Native everywhere: Browser Extensions &amp; Electron</title>
      <dc:creator>Matteo Mazzarolo</dc:creator>
      <pubDate>Tue, 28 Sep 2021 14:07:00 +0000</pubDate>
      <link>https://dev.to/mmazzarolo/running-react-native-everywhere-browser-extensions-electron-46hh</link>
      <guid>https://dev.to/mmazzarolo/running-react-native-everywhere-browser-extensions-electron-46hh</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;Fourth part of the &lt;a href="https://dev.to/mmazzarolo/running-react-native-everywhere-4gpp"&gt;"Running React Native everywhere"&lt;/a&gt; series: a tutorial about structuring your monorepo to run multiple React Native apps targeting different platforms.&lt;/p&gt;

&lt;p&gt;This time, we'll focus on running React Native in an Electron app and in a browser extension.&lt;/p&gt;

&lt;h2&gt;
  
  
  About web-based platforms
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;⚠️ This post is more of a fun experiment than a real tutorial :)&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
I'm not aware of many React Native for Web apps running in Electron in production (besides &lt;a href="https://ordinarypuzzles.com/"&gt;Ordinary Puzzles&lt;/a&gt; and &lt;a href="https://devhubapp.com/"&gt;DevHub&lt;/a&gt;). And I've never heard of anyone running React Native for Web in a browser extension before.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now that &lt;a href="https://dev.to/mmazzarolo/running-react-native-everywhere-the-web-h6d/"&gt;we added support for React Native on the web&lt;/a&gt;, we can leverage web-based frameworks to run our web app on different platforms:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;With &lt;a href="https://www.electronjs.org/"&gt;Electron&lt;/a&gt;, we can build cross-platform desktop apps to run our React Native for Web app.&lt;/li&gt;
&lt;li&gt;With the &lt;a href="https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions"&gt;WebExtension API&lt;/a&gt; (for Firefox) and the Chrome &lt;a href="https://developer.chrome.com/extensions"&gt;Extension API&lt;/a&gt; (for Chrome, Edge, Opera, and Vivaldi), we can run our React Native for Web app in a browser extension.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In both cases, we'll re-use our web app workspace as the foundation.&lt;/p&gt;

&lt;p&gt;If you’re not familiar with web development this section will feel somewhat different from the rest of the tutorial because we won't work with anything really specific to React Native.&lt;br&gt;&lt;br&gt;
This is more about adding support for Electron and a browser extension to a web app. Still, I think it's still a valuable example of &lt;strong&gt;how our React Native JavaScript code can run everywhere&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Electron
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.electronjs.org/"&gt;Electron&lt;/a&gt; is a popular framework for building cross-platform desktop apps with JavaScript, HTML, and CSS.&lt;br&gt;&lt;br&gt;
Many popular apps like Visual Studio Code or Slack are built with Electron.&lt;/p&gt;

&lt;p&gt;Let's start by addressing the elephant in the room: yes, Electron apps can (and often do) perform poorly and not fit in with the rest of the operative system. That said, Electron is still a valid option for shipping desktop apps on platforms not yet supported by React Native (e.g., Linux) or if you don't want to (or can't) deal with Windows/macOS native code.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This tutorial will show you the bare minimum setup required to develop your React Native for Web app on Electron.&lt;br&gt;&lt;br&gt;
If you're interested in a more in-depth tutorial, please check &lt;a href="https://mmazzarolo.com/blog/2021-08-12-building-an-electron-application-using-create-react-app/"&gt;"Building a desktop application using Electron and Create React App"&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Let's start by duplicating the React Native for Web workspace into a new &lt;code&gt;electron&lt;/code&gt; one.&lt;/p&gt;

&lt;p&gt;From the &lt;code&gt;packages/&lt;/code&gt; directory, run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; web electron &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;electron
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add the following dependencies (most of them are here only to simplify the development flow):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yarn add &lt;span class="nt"&gt;-D&lt;/span&gt; concurrently cross-env electron electronmon wait-on
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/open-cli-tools/concurrently"&gt;&lt;code&gt;concurrently&lt;/code&gt;&lt;/a&gt;: Run multiple commands concurrently. We'll use it to run both the Electron process and the React app in watch mode.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/kentcdodds/cross-env"&gt;&lt;code&gt;cross-env&lt;/code&gt;&lt;/a&gt;: Run scripts that set and use environment variables across different platforms. We'll use it to make our scripts compatible with both Unix and Windows OSes.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.electronjs.org/"&gt;&lt;code&gt;electron&lt;/code&gt;&lt;/a&gt;: The core framework for creating the app.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/catdad/electronmon"&gt;&lt;code&gt;electronmon&lt;/code&gt;&lt;/a&gt;: Like &lt;a href="https://github.com/remy/nodemon"&gt;&lt;code&gt;nodemon&lt;/code&gt;&lt;/a&gt;, but for the Electron process. Allows watching and reloading our Electron app.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/jeffbski/wait-on"&gt;&lt;code&gt;wait-on&lt;/code&gt;&lt;/a&gt;: Utility to wait for files, ports, sockets, etc. We'll use it to wait for the React app to be built before we open the Electron app (while developing).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The next step is creating Electron's main script. This script controls the main process, which runs in a full Node.js environment and is responsible for managing your app's lifecycle, displaying native interfaces, performing privileged operations, and managing renderer processes.&lt;/p&gt;

&lt;p&gt;Create a new &lt;code&gt;electron.js&lt;/code&gt; file in &lt;code&gt;public/&lt;/code&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="c1"&gt;// Module to control the application lifecycle and the native browser window.&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;BrowserWindow&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;electron&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;url&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Create the native browser window.&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;createWindow&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mainWindow&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;BrowserWindow&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;800&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;600&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="c1"&gt;// In production, set the initial browser path to the local bundle generated&lt;/span&gt;
  &lt;span class="c1"&gt;// by the Create React App build process.&lt;/span&gt;
  &lt;span class="c1"&gt;// In development, set it to localhost to allow live/hot-reloading.&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;appURL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isPackaged&lt;/span&gt;
    &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;format&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;pathname&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;index.html&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="na"&gt;protocol&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;file:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;slashes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&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;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;http://localhost:3000&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;mainWindow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;loadURL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;appURL&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Automatically open Chrome's DevTools in development mode.&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isPackaged&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;mainWindow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;webContents&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;openDevTools&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;span class="c1"&gt;// This method will be called when Electron has finished its initialization and&lt;/span&gt;
&lt;span class="c1"&gt;// is ready to create the browser windows.&lt;/span&gt;
&lt;span class="c1"&gt;// Some APIs can only be used after this event occurs.&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;whenReady&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;createWindow&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;activate&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// On macOS it's common to re-create a window in the app when the&lt;/span&gt;
    &lt;span class="c1"&gt;// dock icon is clicked and there are no other windows open.&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;BrowserWindow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getAllWindows&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;createWindow&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;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Quit when all windows are closed, except on macOS.&lt;/span&gt;
&lt;span class="c1"&gt;// There, it's common for applications and their menu bar to stay active until&lt;/span&gt;
&lt;span class="c1"&gt;// the user quits  explicitly with Cmd + Q.&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;window-all-closed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;platform&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;darwin&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;quit&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;span class="c1"&gt;// In this file you can include the rest of your app's specific main process&lt;/span&gt;
&lt;span class="c1"&gt;// code. You can also put them in separate files and require them here.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, we need to make a few changes to &lt;code&gt;package.json&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Rename the app from &lt;code&gt;@my-app/web&lt;/code&gt; to &lt;code&gt;@my-app/electron&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Add the &lt;code&gt;main&lt;/code&gt; entry. During execution, Electron will look for the script we created above in the &lt;code&gt;main&lt;/code&gt; field of the app’s &lt;code&gt;package.json&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Update the &lt;code&gt;homepage&lt;/code&gt; property. We need to enforce Create React App to infer a relative root path in the generated HTML file. This is a requirement because we're not going to serve the HTML file; it will be loaded directly by Electron.
To do so, we can set the &lt;code&gt;homepage&lt;/code&gt; property of the &lt;code&gt;package.json&lt;/code&gt; to &lt;code&gt;./&lt;/code&gt; (see &lt;a href="https://create-react-app.dev/docs/deployment/#building-for-relative-paths"&gt;Building For Relative Paths&lt;/a&gt; in the Create React App documentation for more details).&lt;/li&gt;
&lt;li&gt;Define a script to build the Create React App and start the Electron process in watch mode.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt; {
&lt;span class="gd"&gt;-  "name": "@my-app/web",
&lt;/span&gt;&lt;span class="gi"&gt;+  "name": "@my-app/electron",
&lt;/span&gt;   "version": "0.0.0",
   "private": true,
&lt;span class="gi"&gt;+  "homepage": "./",
+  "main": "./public/electron.js",
&lt;/span&gt;   "scripts": {
&lt;span class="gd"&gt;-    "start": "craco start",
&lt;/span&gt;&lt;span class="gi"&gt;+    "start": "concurrently -k \"cross-env BROWSER=none craco start\" \"wait-on http://localhost:3000 &amp;amp;&amp;amp; electronmon .\"",
&lt;/span&gt;     "build": "craco build"
   },
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;start&lt;/code&gt; script might look a bit confusing now, so here's a breakdown of what it does:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;concurrently -k&lt;/code&gt; invokes the subsequent commands in parallel, and kills both of them when the process is stopped.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;cross-env BROWSER=none yarn start&lt;/code&gt; sets the &lt;code&gt;BROWSER=none&lt;/code&gt; environment variables (using &lt;code&gt;cross-env&lt;/code&gt; for Windows compatibility) to disable the automatic opening of the browser and invokes the &lt;code&gt;start&lt;/code&gt; script, which runs the Create React App build in watch-mode.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;wait-on http://localhost:3000 &amp;amp;&amp;amp; electronmon .&lt;/code&gt; waits for the Create React App dev-server to serve the app on localhost:3000, and then invokes &lt;code&gt;electronmon .&lt;/code&gt; to start the Electron add in watch-mode.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ Adding a &lt;code&gt;build&lt;/code&gt; script to our Electron app requires some additional work that, for the sake of simplicity, I glossed over in this blog post. Please check &lt;a href="https://mmazzarolo.com/blog/2021-08-12-building-an-electron-application-using-create-react-app/"&gt;"Building a desktop application using Electron and Create React App"&lt;/a&gt; for a more in-depth guide.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Finally, add the &lt;code&gt;electron:start&lt;/code&gt; script to the root &lt;code&gt;package.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"electron:start"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"yarn workspace @my-app/electron start"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And run it to start developing your Electron app:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ojroVVsv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://mmazzarolo.com/static/d1674fc224c5887f87fcfdbc9ad70580/b1001/screenshot-electron.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ojroVVsv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://mmazzarolo.com/static/d1674fc224c5887f87fcfdbc9ad70580/b1001/screenshot-electron.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Browser Extension
&lt;/h2&gt;

&lt;p&gt;Extensions, or add-ons, can modify and enhance the capability of a browser.&lt;br&gt;&lt;br&gt;
There are two primary standards used for building browser extensions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://developer.chrome.com/extensions"&gt;Chrome/Chromium's extension API&lt;/a&gt;, supported by Chromium-based browsers (such as Google Chrome, Microsoft Edge, Opera, Vivaldi)&lt;/li&gt;
&lt;li&gt;The &lt;a href="https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions"&gt;WebExtensions API&lt;/a&gt;, supported by Firefox addons (and, in a limited way, by the latest version of Safari).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These two technologies are, to a large extent, compatible.&lt;br&gt;
In most cases, extensions written for Chromium-based browsers run in Firefox &lt;a href="https://extensionworkshop.com/documentation/develop/porting-a-google-chrome-extension/"&gt;with just a few changes&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Extensions are created using web-based technologies: HTML, CSS, and JavaScript. They can take advantage of the same web APIs as JavaScript on a web page, but extensions also have access to their own set of JavaScript APIs.&lt;/p&gt;

&lt;p&gt;Since we already have a working web app, we just need a couple of tweaks to use it as the foundation for our browser extension.&lt;/p&gt;

&lt;p&gt;Let's start by duplicating the React Native for Web workspace (&lt;code&gt;packages/web&lt;/code&gt;) into a new &lt;code&gt;packages/browser-ext&lt;/code&gt; one.&lt;/p&gt;

&lt;p&gt;From the &lt;code&gt;packages/&lt;/code&gt; directory, run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; web browser-ext &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;browser-ext
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every browser extension requires &lt;a href="https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Anatomy_of_a_WebExtension"&gt;a manifest (&lt;code&gt;manifest.json&lt;/code&gt;)&lt;/a&gt; to be identified by the browser. A manifest contains basic metadata such as its name, version, and the permissions it requires. It also provides pointers to other files in the extension.&lt;/p&gt;

&lt;p&gt;By default, Create React App creates a &lt;a href="https://developer.mozilla.org/en-US/docs/Web/Manifest"&gt;Web App manifest&lt;/a&gt; in the &lt;code&gt;/public&lt;/code&gt; dir. This default manifest is part of the technologies that power &lt;a href="https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps"&gt;Progressive Web Apps&lt;/a&gt; (PWA) and follows an entirely different standard from the Extension API manifest we need.&lt;/p&gt;

&lt;p&gt;So, let's replace the content of &lt;code&gt;public/manifest.json&lt;/code&gt; with our own extension manifest.&lt;br&gt;
This new manifest tells the browser we're building a &lt;a href="https://developer.chrome.com/docs/extensions/mv3/getstarted/#user_interface"&gt;popup extension&lt;/a&gt; and that its entry point is located at &lt;code&gt;browser-ext/public/index.html&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"My Extension"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.0.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"manifest_version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"browser_action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"default_popup"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"index.html"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, we need a tiny tweak for the &lt;code&gt;start&lt;/code&gt; and &lt;code&gt;build&lt;/code&gt; scripts:&lt;/p&gt;

&lt;p&gt;Out-of-the-box, Create React App embeds an inline script into &lt;code&gt;index.html&lt;/code&gt; of the production build.&lt;br&gt;&lt;br&gt;
This is a small chunk of Webpack runtime logic used to load and run the application, which is embedded in our &lt;code&gt;build/index.html&lt;/code&gt; file to save an additional network request on web apps. Unfortunately, it also breaks the extension usage by violating the web extension API Content Security Policy (CSP), which doesn't allow loading external scripts into the extension.&lt;br&gt;&lt;br&gt;
The easiest way to solve this issue is by turning off the inline script by the &lt;a href="https://facebook.github.io/create-react-app/docs/advanced-configuration"&gt;&lt;code&gt;INLINE_RUNTIME_CHUNK&lt;/code&gt;&lt;/a&gt; environment variable to &lt;code&gt;false&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt; {
&lt;span class="gd"&gt;-  "name": "@my-app/web",
&lt;/span&gt;&lt;span class="gi"&gt;+  "name": "@my-app/browser-ext",
&lt;/span&gt;   "version": "0.0.0",
   "private": true,
   "scripts": {
&lt;span class="gd"&gt;-    "start": "craco start",
&lt;/span&gt;&lt;span class="gi"&gt;+    "start": "INLINE_RUNTIME_CHUNK=false craco start",
&lt;/span&gt;&lt;span class="gd"&gt;-    "build": "craco build",
&lt;/span&gt;&lt;span class="gi"&gt;+    "build": "INLINE_RUNTIME_CHUNK=false craco build"
&lt;/span&gt;   },
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, add the &lt;code&gt;start&lt;/code&gt; and &lt;code&gt;build&lt;/code&gt; script to root's &lt;code&gt;package.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"browser-ext:start"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"yarn workspace @my-app/browser-ext start"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"browser-ext:build"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"yarn workspace @my-app/browser-ext build"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can now run &lt;code&gt;browser-ext:start&lt;/code&gt; and add the browser extension to the browser to develop it (see &lt;a href="https://support.google.com/chrome_webstore/answer/2664769?hl=en"&gt;"Install and manage extensions"&lt;/a&gt; for details):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--K12SvPfi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://mmazzarolo.com/static/b78a911679688706f21c5aa51a5aa831/b1001/screenshot-browser-ext.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--K12SvPfi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://mmazzarolo.com/static/b78a911679688706f21c5aa51a5aa831/b1001/screenshot-browser-ext.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What we've done so far is just the bare minimum work required to make the browser extension run.&lt;br&gt;&lt;br&gt;
As your next step, I'd suggest you to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Clean up the &lt;code&gt;public&lt;/code&gt; dir, making sure to keep there only &lt;code&gt;manifest.json&lt;/code&gt; and &lt;code&gt;index.html&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Remove the Service Worker and the Web Vitals scripts installed by default by Create React App (they won't work in a browser extension).&lt;/li&gt;
&lt;li&gt;Tweak the &lt;code&gt;start&lt;/code&gt; script to &lt;a href="https://mmazzarolo.com/blog/2019-10-19-browser-extension-development/"&gt;enable hot-reloading&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Get familiar with the browser extension APIs (and limitations).&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Compatibility and platform-specific code
&lt;/h2&gt;

&lt;p&gt;As always, please keep in mind that every platform has its limitations.&lt;br&gt;&lt;br&gt;
Be it Electron or a browser extension, we shouldn't expect every API exposed by React Native for Web to work out-of-the-box.&lt;/p&gt;

&lt;p&gt;Something worth noticing is that, even if we're targeting different platforms/frameworks, the React Native &lt;code&gt;Platform&lt;/code&gt; API will always detect the OS as &lt;code&gt;"web"&lt;/code&gt; because it's not aware of whether a React Native for Web app is running in a website, in Electron, or in a browser extension.&lt;br&gt;&lt;br&gt;
A possible workaround for this issue is to inject a more specific target platform as an environment variable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt; const webpack = require("webpack");
 const { getWebpackTools } = require("react-native-monorepo-tools");

 const monorepoWebpackTools = getWebpackTools();

 module.exports = {
   webpack: {
     configure: (webpackConfig) =&amp;gt; {
       // Allow importing from external workspaces.
       monorepoWebpackTools.enableWorkspacesResolution(webpackConfig);
       // Ensure nohoisted libraries are resolved from this workspace.
       monorepoWebpackTools.addNohoistAliases(webpackConfig);
       return webpackConfig;
     },
     plugins: [
       // Inject the "__DEV__" global variable.
       new webpack.DefinePlugin({
         __DEV__: process.env.NODE_ENV !== "production",
       }),
&lt;span class="gi"&gt;+      // Inject the "__SUBPLATFORM__" global variable.
+      new webpack.DefinePlugin({
+        __SUBPLATFORM__: JSON.stringify("electron"), // Or "browser-ext"
+      }),
&lt;/span&gt;     ],
   },
 };
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the &lt;code&gt;app&lt;/code&gt; workspace, we can then check the &lt;code&gt;__SUBPLATFORM__&lt;/code&gt; global variable to detect whether we're running in a web page, in Electron, or in a browser extension.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's next?
&lt;/h2&gt;

&lt;p&gt;When I started writing this series, I envisioned this post as the last one of the tutorials.&lt;br&gt;&lt;br&gt;
Still, in the next few days I'll write a FAQs post to ensure the most common questions and answers about the series are captured in a single location. So, please, stay tuned!&lt;/p&gt;

&lt;p&gt;If you somehow managed to read through this entire series, hats off to you!&lt;br&gt;&lt;br&gt;
I hope what I've shown you may give you some ideas about approaching a multi-platform project of your own.&lt;br&gt;&lt;br&gt;
I surely learned a lot while experimenting with it.&lt;/p&gt;

&lt;p&gt;Thanks to the React + React Native team and community for building all these fantastic tools! ♥&lt;/p&gt;

&lt;p&gt;For feedback and questions, feel free to start a discussion on the &lt;a href="https://github.com/mmazzarolo/react-native-universal-monorepo/discussions"&gt;React Native Universal Monorepo's discussions page&lt;/a&gt; or send me a direct message.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/mmazzarolo/running-react-native-everywhere-4gpp"&gt;Overview&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/mmazzarolo/running-react-native-everywhere-yarn-workspaces-monorepo-3j5g"&gt;Monorepo setup&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/mmazzarolo/running-react-native-everywhere-android-ios-3451"&gt;Android &amp;amp; iOS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/mmazzarolo/running-react-native-everywhere-windows-macos-7lp"&gt;Windows &amp;amp; macOS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/mmazzarolo/running-react-native-everywhere-the-web-h6d/"&gt;The web&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Browser extensions &amp;amp; Electron (☜ you're here)&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://mmazzarolo.com/blog/2021-09-25-running-react-native-everywhere-electron-browser-ext/"&gt;mmazzarolo.com&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>react</category>
      <category>reactnative</category>
    </item>
    <item>
      <title>Running React Native everywhere: The Web</title>
      <dc:creator>Matteo Mazzarolo</dc:creator>
      <pubDate>Sun, 26 Sep 2021 17:17:00 +0000</pubDate>
      <link>https://dev.to/mmazzarolo/running-react-native-everywhere-the-web-h6d</link>
      <guid>https://dev.to/mmazzarolo/running-react-native-everywhere-the-web-h6d</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;Fourth part of the &lt;a href="https://dev.to/mmazzarolo/running-react-native-everywhere-4gpp"&gt;"Running React Native everywhere"&lt;/a&gt; series: a tutorial about structuring your monorepo to run multiple React Native apps targeting different platforms.&lt;/p&gt;

&lt;p&gt;This time, we'll focus on running React Native on the web.&lt;/p&gt;

&lt;h2&gt;
  
  
  About React Native for Web
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/necolas/react-native-web" rel="noopener noreferrer"&gt;React Native for Web&lt;/a&gt;&lt;/strong&gt; is an accessible implementation of React Native's components and APIs that is interoperable with React DOM.&lt;br&gt;&lt;br&gt;
React Native for Web translates all the references of React Native's components (e.g. &lt;code&gt;View&lt;/code&gt;) and APIs to their HTML &amp;amp; DOM counterpart (e.g., &lt;code&gt;div&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://necolas.github.io/react-native-web/" rel="noopener noreferrer"&gt;The React Native for Web homepage&lt;/a&gt; does a great job at highlighting why you should try it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Accessible HTML.&lt;/strong&gt; Support different devices and input modes, render semantic tags.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;High-quality interactions.&lt;/strong&gt; Support gestures and multiple input modes (touch, mouse, keyboard).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reliable styles.&lt;/strong&gt; Rely on scoped styles and automatic vendor-prefixing. Support RTL layouts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Responsive containers.&lt;/strong&gt; Respond to element resize events.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Incremental adoption.&lt;/strong&gt; Interoperates with existing React DOM components. Bundle only what you use.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you've already built a React Native app and you're planning to port it to the web, I recommend giving React Native for Web a try.&lt;/p&gt;

&lt;p&gt;One of the most common mistakes people make about React Native for Web is assuming that it &lt;strong&gt;is&lt;/strong&gt; React Native.&lt;br&gt;&lt;br&gt;
Let's be clear: in a React Native for Web project you're not "using" React Native, you're just aliasing every component and API from &lt;code&gt;react-native&lt;/code&gt; to &lt;code&gt;react-native-web&lt;/code&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="c1"&gt;// Running the following:&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;Button&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Image&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;StyleSheet&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;View&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="s2"&gt;react-native&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// At build time is translated to:&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;Button&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Image&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;StyleSheet&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;View&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="s2"&gt;react-native-web&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;Instead of thinking about React Native for Web as a library for building mobile apps that run on the web, think of it as a website that uses React Native as a "components and API framework".&lt;/p&gt;

&lt;p&gt;Because React Native for Web &lt;strong&gt;is&lt;/strong&gt; a React website, you can use front-end tools to build and run it.&lt;br&gt;&lt;br&gt;
For example, you can build it with &lt;a href="https://webpack.js.org/" rel="noopener noreferrer"&gt;Webpack&lt;/a&gt; or &lt;a href="https://rollupjs.org/guide/en/" rel="noopener noreferrer"&gt;Rollup&lt;/a&gt; instead of &lt;a href="https://facebook.github.io/metro/" rel="noopener noreferrer"&gt;Metro bundler&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Like for React Native for Windows + macOS, you &lt;strong&gt;can&lt;/strong&gt; add React Native for Web to an existing mobile project. I've already written about this option in the past in &lt;a href="https://mmazzarolo.com/blog/2020-10-24-adding-react-native-web/" rel="noopener noreferrer"&gt;"Run your React Native app on the web with React Native for Web"&lt;/a&gt;.&lt;br&gt;&lt;br&gt;
However, in this tutorial we'll add it as a separate &lt;code&gt;web&lt;/code&gt; workspace.&lt;/p&gt;
&lt;h2&gt;
  
  
  Create React App (CRA)
&lt;/h2&gt;

&lt;p&gt;React Native for Web is compatible with multiple frameworks and tools. You can use it with &lt;a href="https://github.com/facebook/create-react-app" rel="noopener noreferrer"&gt;Create React App&lt;/a&gt;, &lt;a href="https://github.com/zeit/next.js/tree/master/examples/with-react-native-web" rel="noopener noreferrer"&gt;Next.js&lt;/a&gt;, &lt;a href="https://github.com/slorber/gatsby-plugin-react-native-web" rel="noopener noreferrer"&gt;Gatsby&lt;/a&gt;, &lt;a href="https://docs.expo.io/workflow/web/" rel="noopener noreferrer"&gt;Expo (!)&lt;/a&gt;, or you can create a custom build process.&lt;/p&gt;

&lt;p&gt;To keep it simple, in this tutorial we'll use Create React App, which is a basic way to setup a simple, web-only React app with built-in support for aliasing &lt;code&gt;react-native-web&lt;/code&gt; to &lt;code&gt;react-native&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Create React App is very limited in its configuration options, so we'll use &lt;a href="https://github.com/gsoft-inc/craco" rel="noopener noreferrer"&gt;CRACO&lt;/a&gt; (Create React App Configuration Override) to customize its Webpack configuration to make it compatible with Yarn workspaces.&lt;/p&gt;
&lt;h2&gt;
  
  
  Setup
&lt;/h2&gt;

&lt;p&gt;First of all, add the &lt;code&gt;react-native-web&lt;/code&gt; library to the &lt;code&gt;nohoist&lt;/code&gt; list in the root's &lt;code&gt;package.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt; {
   "name": "my-app",
   "version": "0.0.1",
   "private": true,
   "workspaces": {
     "packages": [
       "packages/*"
     ],
     "nohoist": [
       "**/react",
       "**/react-dom",
       "**/react-native",
       "**/react-native/**",
       "**/react-native-windows",
&lt;span class="gi"&gt;+      "**/react-native-web"
&lt;/span&gt;     ]
   }
 }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, from the &lt;code&gt;packages&lt;/code&gt; directory, scaffold a new Create React App project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx create-react-app my-app &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;mv &lt;/span&gt;my-app web
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Rename the package name:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt; {
&lt;span class="gd"&gt;-  "name": "my-app",
&lt;/span&gt;&lt;span class="gi"&gt;+  "name": "@my-app/web",
&lt;/span&gt;   "version": "0.0.0",
   "private": true,
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And install &lt;code&gt;react-native-web&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;web &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; yarn add react-native-web
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The cool thing about Create React App is that adding &lt;code&gt;react-native-web&lt;/code&gt; to our dependencies is enough to make it automatically resolve &lt;code&gt;react-native-web&lt;/code&gt; instead of &lt;code&gt;react-native&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To start using our React Native app within the web project, add it to the JavaScript entry point:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt; import React from "react";
 import ReactDOM from "react-dom";
 import "./index.css";
&lt;span class="gd"&gt;-import App from "./App";
&lt;/span&gt;&lt;span class="gi"&gt;+import { App } from "@my-app/app";
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt; ReactDOM.render(
   &amp;lt;React.StrictMode&amp;gt;
     &amp;lt;App /&amp;gt;
   &amp;lt;/React.StrictMode&amp;gt;,
   document.getElementById("root")
 );
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If Create React App supported Yarn workspaces out-of-the-box, what we've done so far would have been enough to run the app... unfortunately, it doesn't.&lt;br&gt;&lt;br&gt;
Luckily, we can use CRACO (or other tools such as &lt;a href="https://github.com/arackaf/customize-cra" rel="noopener noreferrer"&gt;&lt;code&gt;customize-cra&lt;/code&gt;&lt;/a&gt; or &lt;a href="https://github.com/timarney/react-app-rewired" rel="noopener noreferrer"&gt;&lt;code&gt;react-app-rewired&lt;/code&gt;&lt;/a&gt;) to customize the Webpack configuration used by Create React App to resolve packages imported from other workspaces.&lt;/p&gt;

&lt;p&gt;Install CRACO and &lt;a href="https://github.com/mmazzarolo/react-native-monorepo-tools/" rel="noopener noreferrer"&gt;&lt;code&gt;react-native-monorepo-tools&lt;/code&gt;&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yarn add &lt;span class="nt"&gt;-D&lt;/span&gt; @craco/craco react-native-monorepo-tools
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a &lt;code&gt;craco.config.js&lt;/code&gt; file at the root of your &lt;code&gt;web&lt;/code&gt; workspace.&lt;br&gt;&lt;br&gt;
We'll use it to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Update the &lt;code&gt;babel-loader&lt;/code&gt; config used in Webpack to allow importing from directories outside of the &lt;code&gt;web&lt;/code&gt; workspace.&lt;/li&gt;
&lt;li&gt;Use &lt;a href="https://webpack.js.org/configuration/resolve/" rel="noopener noreferrer"&gt;Webpack's aliases&lt;/a&gt; to ensure all the libraries in the &lt;code&gt;nohoist&lt;/code&gt; list are resolved from &lt;code&gt;web/node_modules&lt;/code&gt;. This ensures the build process doesn't bundle the same library twice if its present in multiple workspaces.&lt;/li&gt;
&lt;li&gt;Inject the &lt;code&gt;__DEV__&lt;/code&gt; global variable in the codebase. &lt;code&gt;__DEV__&lt;/code&gt; is commonly used in React Native apps to determine if we're running in development or production mode (like &lt;code&gt;process.env.NODE_ENV&lt;/code&gt; on the web).
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;webpack&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;webpack&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;getWebpackTools&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react-native-monorepo-tools&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;monorepoWebpackTools&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getWebpackTools&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;webpack&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;configure&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;webpackConfig&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// Allow importing from external workspaces.&lt;/span&gt;
      &lt;span class="nx"&gt;monorepoWebpackTools&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;enableWorkspacesResolution&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;webpackConfig&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="c1"&gt;// Ensure nohoisted libraries are resolved from this workspace.&lt;/span&gt;
      &lt;span class="nx"&gt;monorepoWebpackTools&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addNohoistAliases&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;webpackConfig&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;webpackConfig&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="c1"&gt;// Inject the React Native "__DEV__" global variable.&lt;/span&gt;
      &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;webpack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;DefinePlugin&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;__DEV__&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NODE_ENV&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;production&lt;/span&gt;&lt;span class="dl"&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;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;To use the updated Webpack configuration, swap &lt;code&gt;react-scripts&lt;/code&gt; in favour of &lt;code&gt;craco&lt;/code&gt; in the workspace &lt;code&gt;start&lt;/code&gt; and &lt;code&gt;build&lt;/code&gt; scripts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt; {
   "name": "@my-app/web",
   "version": "0.0.0",
   "private": true,
   "scripts": {
&lt;span class="gd"&gt;-    "start": "react-scripts start",
&lt;/span&gt;&lt;span class="gi"&gt;+    "start": "craco start",
&lt;/span&gt;&lt;span class="gd"&gt;-    "build": "react-scripts build",
&lt;/span&gt;&lt;span class="gi"&gt;+    "build": "craco build",
&lt;/span&gt;     "test": "react-scripts test --watchAll=false --passWithNoTests",
     "eject": "react-scripts eject"
   },
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And while you're at it, update the root &lt;code&gt;package.json&lt;/code&gt; so that you can invoke the web scripts from the root of the monorepo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nl"&gt;"web:start"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"yarn workspace @my-app/web start"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"web:build"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"yarn workspace @my-app/web build"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We're done.&lt;br&gt;&lt;br&gt;
We can now run &lt;code&gt;yarn web:start&lt;/code&gt; to run your app in development mode, and &lt;code&gt;yarn web:build&lt;/code&gt; to create a production build.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmmazzarolo.com%2Fstatic%2F8155faa0a45a7cc5a67561f82b0c071b%2Fb1001%2Fscreenshot.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmmazzarolo.com%2Fstatic%2F8155faa0a45a7cc5a67561f82b0c071b%2Fb1001%2Fscreenshot.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Compatibility and platform-specific code
&lt;/h2&gt;

&lt;p&gt;React Native for Web provides compatibility with the vast majority of React Native’s JavaScript API. Features deprecated in React Native should be considered unsupported in React Native for Web.&lt;br&gt;&lt;br&gt;
See &lt;a href="https://necolas.github.io/react-native-web/docs/react-native-compatibility/" rel="noopener noreferrer"&gt;"React Native compatibility"&lt;/a&gt; for details.&lt;/p&gt;

&lt;p&gt;Also, like for Windows and macOS, React Native provides two ways to organize your web-specific code and separate it from the other platforms:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Using the &lt;a href="https://reactnative.dev/docs/platform-specific-code#platform-module" rel="noopener noreferrer"&gt;&lt;code&gt;platform&lt;/code&gt; module&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Using &lt;a href="https://reactnative.dev/docs/platform-specific-code#platform-specific-extensions" rel="noopener noreferrer"&gt;platform-specific file extensions&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  On Expo &amp;amp; Next.js
&lt;/h2&gt;

&lt;p&gt;In this tutorial we're not using &lt;a href="https://expo.io/" rel="noopener noreferrer"&gt;Expo&lt;/a&gt; because it's not compatible (yet) with every platform we're supporting. Still, &lt;a href="https://docs.expo.io/workflow/web/" rel="noopener noreferrer"&gt;Expo for Web&lt;/a&gt; supports React Native for Web out-of-the-box, provides dozens of additional cross-platform APIs, includes web build optimizations, and is compatible with the broader React Native ecosystem.&lt;/p&gt;

&lt;p&gt;And thanks to &lt;a href="https://github.com/expo/expo-cli/tree/master/packages/next-adapter" rel="noopener noreferrer"&gt;&lt;code&gt;@expo/next-adapter&lt;/code&gt;&lt;/a&gt;, you can even use &lt;a href="https://nextjs.org/" rel="noopener noreferrer"&gt;Next.js&lt;/a&gt; to control your Expo for Web app.&lt;br&gt;&lt;br&gt;
For details, check &lt;a href="https://docs.expo.dev/guides/using-nextjs/" rel="noopener noreferrer"&gt;"Using Next.js with Expo for Web"&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next steps
&lt;/h2&gt;

&lt;p&gt;In the next step, we'll re-use the &lt;code&gt;web&lt;/code&gt; codebase we just created as a boilerplate to support &lt;a href="https://www.electronjs.org/" rel="noopener noreferrer"&gt;Electron&lt;/a&gt; &amp;amp; browser extensions.&lt;/p&gt;

&lt;p&gt;Stay tuned!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/mmazzarolo/running-react-native-everywhere-4gpp"&gt;Overview&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/mmazzarolo/running-react-native-everywhere-yarn-workspaces-monorepo-3j5g"&gt;Monorepo setup&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/mmazzarolo/running-react-native-everywhere-android-ios-3451"&gt;Android &amp;amp; iOS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/mmazzarolo/running-react-native-everywhere-windows-macos-7lp"&gt;Windows &amp;amp; macOS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The Web (☜ you're here)&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/mmazzarolo/running-react-native-everywhere-browser-extensions-electron-46hh"&gt;Electron &amp;amp; browser extension&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://mmazzarolo.com/blog/2021-09-22-running-react-native-everywhere-web/" rel="noopener noreferrer"&gt;mmazzarolo.com&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>react</category>
      <category>reactnative</category>
    </item>
    <item>
      <title>Running React Native everywhere: Windows &amp; macOS</title>
      <dc:creator>Matteo Mazzarolo</dc:creator>
      <pubDate>Wed, 22 Sep 2021 11:58:00 +0000</pubDate>
      <link>https://dev.to/mmazzarolo/running-react-native-everywhere-windows-macos-7lp</link>
      <guid>https://dev.to/mmazzarolo/running-react-native-everywhere-windows-macos-7lp</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;Third part of the &lt;a href="https://dev.to/mmazzarolo/running-react-native-everywhere-4gpp"&gt;"Running React Native everywhere"&lt;/a&gt; series: a tutorial about structuring your monorepo to run multiple React Native apps targeting different platforms.&lt;/p&gt;

&lt;p&gt;This time, we'll focus on the &lt;strong&gt;Windows&lt;/strong&gt; and &lt;strong&gt;macOS&lt;/strong&gt; platforms.&lt;/p&gt;

&lt;h2&gt;
  
  
  About React Native for Windows + macOS
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://microsoft.github.io/react-native-windows/" rel="noopener noreferrer"&gt;React Native for Windows + macOS&lt;/a&gt; brings React Native support for the &lt;a href="https://developer.microsoft.com/en-us/windows/downloads" rel="noopener noreferrer"&gt;Windows SDK&lt;/a&gt; as well as the &lt;a href="https://www.apple.com/newsroom/2017/09/macos-high-sierra-now-available-as-a-free-update/" rel="noopener noreferrer"&gt;macOS 10.13 SDK&lt;/a&gt;&lt;/strong&gt;. With this, you can use JavaScript to build native Windows apps for &lt;a href="https://developer.microsoft.com/windows/get-started-windows-10" rel="noopener noreferrer"&gt;all devices supported by Windows 10 and higher&lt;/a&gt; including PCs, tablets, 2-in-1s, Xbox, Mixed reality devices, etc., as well as the macOS desktop and laptop ecosystems.&lt;/p&gt;

&lt;p&gt;The React Native for Windows + macOS development flow is very similar to the Android and iOS one. If you're already familiar with building mobile React Native apps and with the Windows or macOS SDK, you should be able to quickly jump into a React Native for Windows + macOS codebase.&lt;/p&gt;

&lt;p&gt;Both the Windows and macOS platforms are currently being maintained by Microsoft.&lt;br&gt;&lt;br&gt;
As of today, React Native for Windows is in a much more stable shape than React Native for macOS, but they're both getting better and better.&lt;/p&gt;

&lt;p&gt;The React Native for Windows + macOS documentation follows a classic approach to setup the projects: it shows you how to add them directly in an existing React Native mobile app, resulting in having the Android, iOS, macOS, and Windows code located in the same directory, sharing a single metro bundler setup.&lt;br&gt;&lt;br&gt;
As explained in the &lt;a href="https://dev.to/mmazzarolo/running-react-native-everywhere-yarn-workspaces-monorepo-3j5g"&gt;monorepo setup guide&lt;/a&gt;, we'll follow a slightly different approach and create a workspace for each platform. By doing so, &lt;strong&gt;we're making our codebase a bit more complex in exchange for a simplified incremental React Native update path&lt;/strong&gt;, because we won't be forced to use the same React Native version on all platforms.  &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Please keep in mind that this solution is probably a bit over-engineered if you &lt;strong&gt;do&lt;/strong&gt; already expect to always use the same React Native version on all platforms. In that case, I'd suggest you to go with the classic approach shown in the React Native for Windows + macOS documentation 👍&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To add support for the Windows and macOS platforms to our monorepo, we'll follow the same pattern we used with the mobile app, creating a workspace for each platform:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
└── &amp;lt;project-root&amp;gt;/
    └── packages/
        &lt;span class="c"&gt;# React Native JavaScript code shared across the apps&lt;/span&gt;
        ├── app/
        │   ├── src/
        │   └── package.json
        &lt;span class="c"&gt;# macOS app configuration files and native code&lt;/span&gt;
        └── macos/
        │   ├── macos/
        │   ├── index.js
        │   ├── metro.config.js
        │   └── package.json
        &lt;span class="c"&gt;# Android/iOS app configuration files and native code&lt;/span&gt;
        └── mobile/
        │   ├── android/
        │   ├── ios/
        │   ├── index.js
        │   ├── metro.config.js
        │   └── package.json
        &lt;span class="c"&gt;# Windows app configuration files and native code&lt;/span&gt;
        └── windows/
            ├── windows/
            ├── index.js
            ├── metro.config.js
            └── package.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Something worth noticing is that React Native for Windows + macOS uses metro bundler, just like React Native mobile does.&lt;br&gt;&lt;br&gt;
So we can leverage the same monorepo tooling we used in our mobile app! 💥&lt;/p&gt;
&lt;h2&gt;
  
  
  Windows
&lt;/h2&gt;

&lt;p&gt;To create the &lt;code&gt;windows&lt;/code&gt; workspace we'll follow the same procedure we used for the &lt;code&gt;mobile&lt;/code&gt; one.&lt;/p&gt;

&lt;p&gt;First of all, add the &lt;code&gt;react-native-windows&lt;/code&gt; library to the &lt;code&gt;nohoist&lt;/code&gt; list in the root's &lt;code&gt;package.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt; {
   "name": "my-app",
   "version": "0.0.1",
   "private": true,
   "workspaces": {
     "packages": [
       "packages/*"
     ],
     "nohoist": [
       "**/react",
       "**/react-dom",
       "**/react-native",
       "**/react-native/**",
&lt;span class="gi"&gt;+      "**/react-native-windows",
&lt;/span&gt;     ]
   }
 }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, from the &lt;code&gt;packages&lt;/code&gt; directory, scaffold a new React Native for Windows project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx react-native init MyApp &lt;span class="nt"&gt;--template&lt;/span&gt; react-native@^0.65.0 &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;mv &lt;/span&gt;MyApp windows
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Update &lt;code&gt;windows/package.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt; {
&lt;span class="gd"&gt;-  "name": "MyApp",
&lt;/span&gt;&lt;span class="gi"&gt;+  "name": "@my-app/windows",
&lt;/span&gt;   "version": "0.0.1",
   "private": true,
   "scripts": {
     "android": "react-native run-android",
     "ios": "react-native run-ios",
     "start": "react-native start",
     "test": "jest",
     "lint": "eslint ."
   },
   "dependencies": {
&lt;span class="gi"&gt;+    "@my-app/app": "*",
&lt;/span&gt;     "react": "17.0.2",
     "react-native": "0.65.1"
   }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Update &lt;code&gt;windows/index.js&lt;/code&gt; to point to our &lt;code&gt;app&lt;/code&gt; workspace:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt; import { AppRegistry } from "react-native";
&lt;span class="gd"&gt;-import App from "./App";
&lt;/span&gt;&lt;span class="gi"&gt;+import App from "@my-app/app";
&lt;/span&gt; import { name as appName } from "./app.json";
&lt;span class="err"&gt;
&lt;/span&gt; AppRegistry.registerComponent(appName, () =&amp;gt; App);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And finalize the Windows project setup:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Install &lt;a href="https://microsoft.github.io/react-native-windows/docs/rnw-dependencies" rel="noopener noreferrer"&gt;all the required dependencies&lt;/a&gt;. Microsoft has done a phenomenal job here: you can check and install all the development dependencies &lt;a href="https://microsoft.github.io/react-native-windows/docs/rnw-dependencies#install-the-development-dependencies" rel="noopener noreferrer"&gt;with a single script&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Install &lt;a href="https://microsoft.github.io/react-native-windows/docs/getting-started#install-the-windows-extension" rel="noopener noreferrer"&gt;the Windows extensions&lt;/a&gt;. This process will add the &lt;code&gt;windows&lt;/code&gt; directory (with the native Windows SDK code) to the workspace and update the metro configuration to support the Windows platform.&lt;/li&gt;
&lt;li&gt;Remove the &lt;code&gt;ios&lt;/code&gt; and &lt;code&gt;android&lt;/code&gt; directories from the workspace.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Last but not least, use &lt;a href="https://github.com/mmazzarolo/react-native-monorepo-tools" rel="noopener noreferrer"&gt;&lt;code&gt;react-native-monorepo-tools&lt;/code&gt;&lt;/a&gt; to make metro compatible with Yarn Workspaces:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="p"&gt;const path = require("path");
const exclusionList = require("metro-config/src/defaults/exclusionList");
const { getMetroConfig } = require("react-native-monorepo-tools");
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gi"&gt;+// Get the metro settings to make it compatible with Yarn workspaces.
+const monorepoMetroConfig = getMetroConfig({
+  reactNativeAlias: "react-native-windows",
+});
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="p"&gt;module.exports = {
&lt;/span&gt;  resolver: {
    blockList: exclusionList([
      // This stops "react-native run-windows" from causing the metro server to crash if its already running
      new RegExp(
        `${path.resolve(__dirname, "windows").replace(/[/\\]/g, "/")}.*`
      ),
      // This prevents "react-native run-windows" from hitting: EBUSY: resource busy or locked, open msbuild.ProjectImports.zip
      /.*\.ProjectImports\.zip/,
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gi"&gt;+     // Ensure we resolve nohoist libraries from this directory.
+     ...monorepoMetroConfig.blockList,
&lt;/span&gt;    ]),
&lt;span class="gi"&gt;+   // Ensure we resolve nohoist libraries from this directory.
+   extraNodeModules: monorepoMetroConfig.extraNodeModules,
&lt;/span&gt;  },
&lt;span class="gi"&gt;+ // Add additional Yarn workspace package roots to the module map.
+ // This allows importing from any workspace.
+ watchFolders: monorepoMetroConfig.watchFolders,
&lt;/span&gt;  transformer: {
    getTransformOptions: async () =&amp;gt; ({
      transform: {
        experimentalImportSupport: false,
        inlineRequires: true,
      },
    }),
  },
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That should be it! We can now run &lt;code&gt;yarn windows&lt;/code&gt; from the &lt;code&gt;windows&lt;/code&gt; workspace to run the app.&lt;/p&gt;

&lt;h2&gt;
  
  
  macOS
&lt;/h2&gt;

&lt;p&gt;Like for Windows setup, to create the &lt;code&gt;macos&lt;/code&gt; workspace we'll follow the same procedure we used for the &lt;code&gt;mobile&lt;/code&gt; one.&lt;/p&gt;

&lt;p&gt;The main difference here is that, as of today, &lt;strong&gt;the latest stable version available for React Native for macOS is 0.63&lt;/strong&gt;.&lt;br&gt;&lt;br&gt;
So we need to take into account that our app will run on two different React Native versions: &lt;code&gt;0.65&lt;/code&gt; for Android, iOS, and Windows, and &lt;code&gt;0.63&lt;/code&gt; for macOS.&lt;/p&gt;

&lt;p&gt;Let's start by adding the &lt;code&gt;react-native-macos&lt;/code&gt; library to the &lt;code&gt;nohoist&lt;/code&gt; list in the root's &lt;code&gt;package.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;{
  "name": "my-app",
  "version": "0.0.1",
  "private": true,
  "workspaces": {
    "packages": [
      "packages/*"
    ],
    "nohoist": [
      "**/react",
      "**/react-dom",
      "**/react-native",
      "**/react-native/**",
&lt;span class="gi"&gt;+     "**/react-native-macos",
&lt;/span&gt;      "**/react-native-windows"
    ]
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, from the &lt;code&gt;packages&lt;/code&gt; directory, scaffold a new React Native for macOS project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx react-native init MyApp &lt;span class="nt"&gt;--template&lt;/span&gt; react-native@^0.65.0 &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;mv &lt;/span&gt;MyApp macos
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Update &lt;code&gt;macos/package.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt; {
&lt;span class="gd"&gt;-  "name": "MyApp",
&lt;/span&gt;&lt;span class="gi"&gt;+  "name": "@my-app/macos",
&lt;/span&gt;   "version": "0.0.1",
   "private": true,
   "scripts": {
     "android": "react-native run-android",
     "ios": "react-native run-ios",
     "start": "react-native start",
     "test": "jest",
     "lint": "eslint ."
   },
   "dependencies": {
&lt;span class="gi"&gt;+    "@my-app/app": "*",
&lt;/span&gt;     "react": "16.13.1",
     "react-native": "0.63.0"
   }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Update &lt;code&gt;macos/index.js&lt;/code&gt; to point to our &lt;code&gt;app&lt;/code&gt; workspace:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt; import { AppRegistry } from "react-native";
&lt;span class="gd"&gt;-import App from "./App";
&lt;/span&gt;&lt;span class="gi"&gt;+import App from "@my-app/app";
&lt;/span&gt; import { name as appName } from "./app.json";
&lt;span class="err"&gt;
&lt;/span&gt; AppRegistry.registerComponent(appName, () =&amp;gt; App);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And finalize the macOS project setup:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Install &lt;a href="https://microsoft.github.io/react-native-windows/docs/0.63/rnm-getting-started#install-the-macos-extension" rel="noopener noreferrer"&gt;the macOS extensions&lt;/a&gt;. This process will add the &lt;code&gt;macos&lt;/code&gt; directory (with the native macOS SDK code) to the workspace and update the metro configuration to support the macOS platform.&lt;/li&gt;
&lt;li&gt;Remove the &lt;code&gt;ios&lt;/code&gt; and &lt;code&gt;android&lt;/code&gt; directories from the workspace.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Last but not least, use &lt;code&gt;react-native-monorepo-tools&lt;/code&gt; to make metro compatible with Yarn Workspaces:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="p"&gt;const path = require("path");
const exclusionList = require("metro-config/src/defaults/exclusionList");
const { getMetroConfig } = require("react-native-monorepo-tools");
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gi"&gt;+// Get the metro settings to make it compatible with Yarn workspaces.
+const monorepoMetroConfig = getMetroConfig({
+  reactNativeAlias: "react-native-macos",
+});
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="p"&gt;module.exports = {
&lt;/span&gt;  transformer: {
    getTransformOptions: async () =&amp;gt; ({
      transform: {
        experimentalImportSupport: false,
        inlineRequires: true,
      },
    }),
  },
&lt;span class="gi"&gt;+ // Add additional Yarn workspace package roots to the module map.
+ // This allows importing from any workspace.
+ watchFolders: monorepoMetroConfig.watchFolders,
+ resolver: {
+   // Ensure we resolve nohoist libraries from this directory.
+   blacklistRE: exclusionList(monorepoMetroConfig.blockList),
+   extraNodeModules: monorepoMetroConfig.extraNodeModules,
+ },
&lt;/span&gt;};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run &lt;code&gt;yarn macos&lt;/code&gt; (from the &lt;code&gt;macos&lt;/code&gt; workspace) et voilà, our React Native app is now running on macOS!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmmazzarolo.com%2Fstatic%2Fc618cffccc8665dcaf1fb392767b6810%2F7321b%2Fmacos.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmmazzarolo.com%2Fstatic%2Fc618cffccc8665dcaf1fb392767b6810%2F7321b%2Fmacos.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  On supporting different React Native versions
&lt;/h2&gt;

&lt;p&gt;Generally, supporting different React Native versions might sound complicated.&lt;br&gt;&lt;br&gt;
From my experience, though, it will rarely be a problem. We only have to worry about breaking changes of React Native JavaScript API/components, which aren't &lt;strong&gt;that&lt;/strong&gt; common nowadays.&lt;br&gt;&lt;br&gt;
And, even if it happens, let's keep in mind that we can always &lt;a href="https://reactnative.dev/docs/platform-specific-code" rel="noopener noreferrer"&gt;encapsulate platform-specific code in multiple ways&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Root-level scripts
&lt;/h2&gt;

&lt;p&gt;Just like we did for the &lt;code&gt;mobile&lt;/code&gt; package, I recommend adding a few scripts to the top-level &lt;code&gt;package.json&lt;/code&gt; to invoke workspace-specific scripts (to avoid having to &lt;code&gt;cd&lt;/code&gt; into a directory every time you need to run a script).&lt;/p&gt;

&lt;p&gt;Add the following scripts to the Windows workspace:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"scripts": {
  "start": "react-native start",
  "windows": "react-native run-windows"
},
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the following scripts to the macOS workspace:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"scripts": {
  "macos": "react-native run-macos",
  "xcode": "xed macos",
  "start": "react-native start",
},
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And then you can reference them from the project root this way:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"scripts": {
  "macos:metro": "yarn workspace @my-app/macos start",
  "macos:start": "yarn workspace @my-app/macos macos",
  "macos:xcode": "yarn workspace @my-app/macos xcode",
  "windows:start": "yarn workspace @my-app/windows windows",
  "windows:metro": "yarn workspace @my-app/windows start"
},
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Compatibility and platform-specific code
&lt;/h2&gt;

&lt;p&gt;React Native for Windows + macOS provides compatibility with the vast majority of React Native’s JavaScript API. Features deprecated in React Native should be considered unsupported in React Native for Windows + macOS.&lt;br&gt;&lt;br&gt;
See &lt;a href="https://microsoft.github.io/react-native-windows/docs/parity-status" rel="noopener noreferrer"&gt;"API Parity"&lt;/a&gt; for details.&lt;/p&gt;

&lt;p&gt;Also, React Native provides two ways to organize your Windows-specific and macOS-specific code and separate it from the other platforms:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Using the &lt;a href="https://reactnative.dev/docs/platform-specific-code#platform-module" rel="noopener noreferrer"&gt;&lt;code&gt;platform&lt;/code&gt; module&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Using &lt;a href="https://reactnative.dev/docs/platform-specific-code#platform-specific-extensions" rel="noopener noreferrer"&gt;platform-specific file extensions&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Next steps
&lt;/h2&gt;

&lt;p&gt;In the next step, we'll add support for the web to our monorepo.&lt;/p&gt;

&lt;p&gt;Stay tuned!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/mmazzarolo/running-react-native-everywhere-4gpp"&gt;Overview&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/mmazzarolo/running-react-native-everywhere-yarn-workspaces-monorepo-3j5g"&gt;Monorepo setup&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/mmazzarolo/running-react-native-everywhere-android-ios-3451"&gt;Android &amp;amp; iOS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Windows &amp;amp; macOS (☜ you’re here)&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/mmazzarolo/running-react-native-everywhere-the-web-h6d"&gt;The Web&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/mmazzarolo/running-react-native-everywhere-browser-extensions-electron-46hh"&gt;Electron &amp;amp; browser extension&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://mmazzarolo.com/blog/2021-09-19-running-react-native-everywhere-windows-macos/" rel="noopener noreferrer"&gt;mmazzarolo.com&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>react</category>
      <category>reactnative</category>
    </item>
    <item>
      <title>Running React Native everywhere: Android &amp; iOS</title>
      <dc:creator>Matteo Mazzarolo</dc:creator>
      <pubDate>Tue, 21 Sep 2021 12:21:00 +0000</pubDate>
      <link>https://dev.to/mmazzarolo/running-react-native-everywhere-android-ios-3451</link>
      <guid>https://dev.to/mmazzarolo/running-react-native-everywhere-android-ios-3451</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;Second part of the &lt;a href="https://dev.to/mmazzarolo/running-react-native-everywhere-4gpp"&gt;"Running React Native everywhere"&lt;/a&gt; series: a tutorial about structuring your project to run multiple React Native apps targeting different platforms.&lt;/p&gt;

&lt;p&gt;This time, we'll build a modular React Native app using a Yarn Workspaces monorepo, starting from Android &amp;amp; iOS.&lt;/p&gt;

&lt;h2&gt;
  
  
  The next step
&lt;/h2&gt;

&lt;p&gt;Now that the monorepo foundation is in place, we can start building our app.&lt;br&gt;&lt;br&gt;
The next step is encapsulating &lt;strong&gt;the shared React Native code&lt;/strong&gt; and &lt;strong&gt;the native Android &amp;amp; iOS code&lt;/strong&gt; in two different workspaces:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
└── &amp;lt;project-root&amp;gt;/
    └── packages/
        &lt;span class="c"&gt;# React Native JavaScript code shared across the apps&lt;/span&gt;
        ├── app/
        │   ├── src/
        │   └── package.json
        &lt;span class="c"&gt;# Android/iOS app configuration files and native code&lt;/span&gt;
        └── mobile/
            ├── android/
            ├── ios/
            ├── app.json
            ├── babel.config.js
            ├── index.js
            ├── metro.config.js
            └── package.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The shared React Native JavaScript code: &lt;code&gt;packages/app&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Lets' start from the shared React Native JavaScript code.&lt;br&gt;&lt;br&gt;
The idea here is to isolate the JavaScript code that runs the app in an &lt;code&gt;app&lt;/code&gt; workspace.&lt;br&gt;&lt;br&gt;
We should think about this workspaces as a standard npm library that can work in isolation.&lt;br&gt;&lt;br&gt;
So it will have its own &lt;code&gt;package.json&lt;/code&gt; where we'll explicitly declare its dependencies.&lt;/p&gt;

&lt;p&gt;Let's start by creating the new package directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;packages/app &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;packages/app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And its &lt;code&gt;package.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"@my-app/app"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0.0.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"private"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"src"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"peerDependencies"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"react"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"*"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"react-native"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"*"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As we already explained in the monorepo setup, we're setting &lt;code&gt;react&lt;/code&gt; and &lt;code&gt;react-native&lt;/code&gt; as &lt;code&gt;peerDependencies&lt;/code&gt; because we expect each app that depends on our package to provide their versions of these libraries.&lt;/p&gt;

&lt;p&gt;Then, let's create a tiny app in &lt;code&gt;src/app.js&lt;/code&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="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;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="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;Image&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;Platform&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;SafeAreaView&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;StyleSheet&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;View&lt;/span&gt;&lt;span class="p"&gt;,&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="s2"&gt;react-native&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;LogoSrc&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./logo.png&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;App&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;SafeAreaView&lt;/span&gt; &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;root&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Image&lt;/span&gt; &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;logo&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;LogoSrc&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Text&lt;/span&gt; &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&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;Hello&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="nx"&gt;Native&lt;/span&gt;&lt;span class="o"&gt;!&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Text&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;View&lt;/span&gt; &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;platformRow&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Text&lt;/span&gt; &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&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;Platform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Text&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;View&lt;/span&gt; &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;platformBackground&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Text&lt;/span&gt; &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;platformValue&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;Platform&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;OS&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Text&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/View&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/View&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/SafeAreaView&lt;/span&gt;&lt;span class="err"&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;styles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;StyleSheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;root&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;100%&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;alignItems&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;center&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;justifyContent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;center&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;backgroundColor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;white&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;logo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;120&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;120&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;marginBottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;fontSize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;28&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;fontWeight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;600&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;platformRow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;marginTop&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;flexDirection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;row&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;alignItems&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;center&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;platformValue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;fontSize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;28&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;fontWeight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;500&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;platformBackground&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;backgroundColor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#ececec&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;borderWidth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;StyleSheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hairlineWidth&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;borderColor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#d4d4d4&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;paddingHorizontal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;borderRadius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;alignItems&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;center&lt;/span&gt;&lt;span class="dl"&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;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Thanks to Yarn Workspaces, we can now use &lt;code&gt;@my-app/app&lt;/code&gt; in any other worskpace by:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Marking &lt;code&gt;@my-app/app&lt;/code&gt; as a dependency&lt;/li&gt;
&lt;li&gt;Importing &lt;code&gt;App&lt;/code&gt;: &lt;code&gt;import App from "@my-app/app";&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The native mobile code and configuration
&lt;/h2&gt;

&lt;p&gt;Now that the shared React Native code is ready let's create &lt;code&gt;packages/mobile&lt;/code&gt;. This workspace will store the Android &amp;amp; iOS native code and import &amp;amp; run &lt;code&gt;packages/app&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Using &lt;a href="https://reactnative.dev/docs/environment-setup" rel="noopener noreferrer"&gt;React Native CLI&lt;/a&gt;, bootstrap a new React Native app within the &lt;code&gt;packages&lt;/code&gt; directory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cd packages &amp;amp;&amp;amp; npx react-native init MyApp &amp;amp;&amp;amp; mv MyApp mobile
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;React Native CLI requires a pascal case name for the generated app — which is why we're using &lt;code&gt;MyApp&lt;/code&gt; and then renaming the directory to &lt;code&gt;mobile&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Then, update the generated &lt;code&gt;package.json&lt;/code&gt; by setting the new package name and adding the &lt;code&gt;@my-app/app&lt;/code&gt; dependency:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt; {
&lt;span class="gd"&gt;-  "name": "MyApp",
&lt;/span&gt;&lt;span class="gi"&gt;+  "name": "@my-app/mobile",
&lt;/span&gt;   "version": "0.0.1",
   "private": true,
   "scripts": {
     "android": "react-native run-android",
     "ios": "react-native run-ios",
     "start": "react-native start",
     "test": "jest",
     "lint": "eslint ."
   },
   "dependencies": {
&lt;span class="gi"&gt;+    "@my-app/app": "*",
&lt;/span&gt;     "react": "17.0.2",
     "react-native": "0.65.1"
   },
   "devDependencies": {
     "@babel/core": "^7.12.9",
     "@babel/runtime": "^7.12.5",
     "babel-jest": "^26.6.3",
     "eslint": "7.14.0",
     "get-yarn-workspaces": "^1.0.2",
     "jest": "^26.6.3",
     "metro-react-native-babel-preset": "^0.66.0",
     "react-native-codegen": "^0.0.7",
     "react-test-renderer": "17.0.2"
   },
   "jest": {
     "preset": "react-native"
   }
 }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, update &lt;code&gt;packages/mobile/index.js&lt;/code&gt; to use &lt;code&gt;@my-app/app&lt;/code&gt; instead of the app template shipped with React Native:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt; import { AppRegistry } from "react-native";
&lt;span class="gd"&gt;-import App from "./App";
&lt;/span&gt;&lt;span class="gi"&gt;+import App from "@my-app/app";
&lt;/span&gt; import { name as appName } from "./app.json";
&lt;span class="err"&gt;
&lt;/span&gt; AppRegistry.registerComponent(appName, () =&amp;gt; App);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Updating the &lt;code&gt;nohoist&lt;/code&gt; list
&lt;/h2&gt;

&lt;p&gt;We should be ready to run the app now, right?&lt;br&gt;&lt;br&gt;
Well... kinda. We still need to update the &lt;code&gt;nohoist&lt;/code&gt; section of the root &lt;code&gt;package.json&lt;/code&gt; to include all the libraries required by React Native.&lt;/p&gt;

&lt;p&gt;To understand why we need to do so, try installing the iOS pods:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;packages/mobile/ios &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; pod &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The command will fail with an error like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;[!]&lt;/span&gt; Invalid Podfile file: cannot load such file:/Users/me/workspace/react-native-universal-monorepo -&amp;gt; js/packages/mobile/node_modules/@react-native-community/cli-platform-ios/native_modules.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As we explained in the previous post, by default Yarn Workspaces will install the dependencies of each package (&lt;code&gt;app&lt;/code&gt;, &lt;code&gt;mobile&lt;/code&gt;, etc.) in &lt;code&gt;&amp;lt;project-root&amp;gt;/node_modules&lt;/code&gt; (AKA "hoisting").&lt;br&gt;&lt;br&gt;
This behaviour doesn't work well with React Native, because &lt;strong&gt;the native code located in &lt;code&gt;mobile/ios&lt;/code&gt; and &lt;code&gt;mobile/android&lt;/code&gt; in some cases references libraries from &lt;code&gt;mobile/node_modules&lt;/code&gt; instead of &lt;code&gt;&amp;lt;project-root&amp;gt;/node_modules&lt;/code&gt;&lt;/strong&gt;.&lt;br&gt;&lt;br&gt;
Luckily, we can opt-out of Yarn workspaces' hoisting for specific libraries by adding them to the &lt;code&gt;nohoist&lt;/code&gt; setting in the root &lt;code&gt;package.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt; {
   "name": "my-app",
   "version": "0.0.1",
   "private": true,
   "workspaces": {
     "packages": [
       "packages/*"
     ],
     "nohoist": [
       "**/react",
       "**/react-dom",
&lt;span class="gi"&gt;+      "**/react-native",
+      "**/react-native/**"
&lt;/span&gt;     ]
   }
 }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Adding the libraries from the diff above should be enough to make an app bootstrapped with React Native 0.65 work correctly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;**/react-native&lt;/code&gt; tells Yarn that the &lt;code&gt;react-native&lt;/code&gt; library should not be hoisted.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;**/react-native/**&lt;/code&gt; tells Yarn that the all &lt;code&gt;react-native&lt;/code&gt;'s dependencies (e.g., &lt;code&gt;metro&lt;/code&gt;, &lt;code&gt;react-native-cli&lt;/code&gt;, etc.) should not be hoisted.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;You &lt;strong&gt;can&lt;/strong&gt; completely opt-out from hoisting on all libraries (e.g., with &lt;code&gt;"nohoist": ["**/**"]&lt;/code&gt;), but I wouldn't advise doing so unless you feel like maintaining the list of hoisted dependencies becomes a burden.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Once you've updated the &lt;code&gt;nohoist&lt;/code&gt; list, run &lt;code&gt;yarn reset &amp;amp;&amp;amp; yarn&lt;/code&gt; from the project root to re-install the dependencies using the updated settings.&lt;/p&gt;

&lt;p&gt;Now &lt;code&gt;cd packages/mobile/ios &amp;amp;&amp;amp; pod install&lt;/code&gt; should install pods correctly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Making metro bundler compatible with Yarn workspaces
&lt;/h2&gt;

&lt;p&gt;Before we can run the app, we still need do one more thing: make &lt;a href="https://facebook.github.io/metro/" rel="noopener noreferrer"&gt;metro bundler&lt;/a&gt; compatible with Yarn workspaces' hoisting.&lt;/p&gt;

&lt;p&gt;Metro bundler is the JavaScript bundler currently used by React Native.&lt;br&gt;&lt;br&gt;
One of metro's most famous limitations (and &lt;a href="https://github.com/facebook/metro/issues/1" rel="noopener noreferrer"&gt;issue number #1 in its GitHub repository&lt;/a&gt;) is its &lt;strong&gt;inability to follow symlinks&lt;/strong&gt;.&lt;br&gt;&lt;br&gt;
Therefore, since all hoisted libraries (basically all libraries not specified in the &lt;code&gt;nohoist&lt;/code&gt; list) are installed in &lt;code&gt;mobile/node_modules&lt;/code&gt; as symlinks from &lt;code&gt;&amp;lt;root&amp;gt;/node_modules&lt;/code&gt;, metro won't be able to detect them.&lt;br&gt;&lt;br&gt;
Additionally, because of this issue, &lt;strong&gt;metro won't even be able to resolve other workspaces (e.g., &lt;code&gt;@my-app/app&lt;/code&gt;) since they're outside of the &lt;code&gt;mobile&lt;/code&gt; directory&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;For example, running the app on iOS will now show the following (or a similar) error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;error: Error: Unable to resolve module @babel/runtime/helpers/interopRequireDefault from /Users/me/workspace/react-native-universal-monorepo-js/packages/mobile/index.js: @babel/runtime/helpers/interopRequireDefault could not be found within the project or in these directories:
  node_modules
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this specific case, metro is telling us that he's unable to find the &lt;code&gt;@babel/runtime&lt;/code&gt; library in &lt;code&gt;mobile/node_modules&lt;/code&gt;. And rightfully so: &lt;code&gt;@babel/runtime&lt;/code&gt; is not part of our &lt;code&gt;nohoist&lt;/code&gt; list, so it will probably be installed in &lt;code&gt;&amp;lt;root&amp;gt;/node_modules&lt;/code&gt; instead of &lt;code&gt;mobile/node_modules&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Luckily, we have &lt;a href="https://facebook.github.io/metro/docs/configuration" rel="noopener noreferrer"&gt;several metro configuration options at our disposal&lt;/a&gt; to fix this problem.&lt;/p&gt;

&lt;p&gt;With the help of a couple of tools, &lt;strong&gt;we can update the metro configuration file (&lt;code&gt;mobile/metro.config.js&lt;/code&gt;) to make metro aware of &lt;code&gt;node_modules&lt;/code&gt;directories available outside of the &lt;code&gt;mobile&lt;/code&gt; directory&lt;/strong&gt; (so that it can resolve &lt;code&gt;@my-app/app&lt;/code&gt;)... with the caveat that &lt;strong&gt;libraries from the &lt;code&gt;nohoist&lt;/code&gt; list should always be resolved from &lt;code&gt;mobile/node_modules&lt;/code&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;To do so, install &lt;a href="https://github.com/mmazzarolo/react-native-monorepo-tools" rel="noopener noreferrer"&gt;&lt;code&gt;react-native-monorepo-tools&lt;/code&gt;&lt;/a&gt;, a set of utilities for making metro compatible with Yarn workspaces based on our &lt;code&gt;nohoist&lt;/code&gt; list.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yarn add &lt;span class="nt"&gt;-D&lt;/span&gt; react-native-monorepo-tools
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And update the metro config:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt; const path = require("path");
 const exclusionList = require("metro-config/src/defaults/exclusionList");
 const { getMetroConfig } = require("react-native-monorepo-tools");
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gi"&gt;+const yarnWorkspacesMetroConfig = getMetroConfig();
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt; module.exports = {
   transformer: {
     getTransformOptions: async () =&amp;gt; ({
       transform: {
         experimentalImportSupport: false,
         inlineRequires: false,
       },
     }),
   },
&lt;span class="gi"&gt;+  // Add additional Yarn workspace package roots to the module map.
+  // This allows importing importing from all the project's packages.
+  watchFolders: yarnWorkspacesMetroConfig.watchFolders,
+  resolver: {
+    // Ensure we resolve nohoist libraries from this directory.
+    blockList: exclusionList(yarnWorkspacesMetroConfig.blockList),
+    extraNodeModules: yarnWorkspacesMetroConfig.extraNodeModules,
+  },
&lt;/span&gt; };
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's how the new settings look like under-the-hood:&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;const&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;path&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;exclusionList&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;metro-config/src/defaults/exclusionList&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;getMetroConfig&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react-native-monorepo-tools&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;yarnWorkspacesMetroConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getMetroConfig&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;transformer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;getTransformOptions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;experimentalImportSupport&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="na"&gt;inlineRequires&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="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="c1"&gt;// Add additional Yarn workspaces to the module map.&lt;/span&gt;
  &lt;span class="c1"&gt;// This allows importing importing from all the project's packages.&lt;/span&gt;
  &lt;span class="na"&gt;watchFolders&lt;/span&gt;&lt;span class="p"&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;/Users/me/my-app/node_modules&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;/Users/me/my-app/packages/app/&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;/Users/me/my-app/packages/build-tools/&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;/Users/me/my-app/packages/mobile/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;resolver&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Ensure we resolve nohoist libraries from this directory.&lt;/span&gt;
    &lt;span class="c1"&gt;// With "((?!mobile).)", we're blocking all the cases were metro tries to&lt;/span&gt;
    &lt;span class="c1"&gt;// resolve nohoisted libraries from a directory that is not "mobile".&lt;/span&gt;
    &lt;span class="na"&gt;blockList&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;exclusionList&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
      &lt;span class="sr"&gt;/^&lt;/span&gt;&lt;span class="se"&gt;((?!&lt;/span&gt;&lt;span class="sr"&gt;mobile&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="sr"&gt;.&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="sr"&gt;*&lt;/span&gt;&lt;span class="se"&gt;\/&lt;/span&gt;&lt;span class="sr"&gt;node_modules&lt;/span&gt;&lt;span class="se"&gt;\/&lt;/span&gt;&lt;span class="sr"&gt;@react-native-community&lt;/span&gt;&lt;span class="se"&gt;\/&lt;/span&gt;&lt;span class="sr"&gt;cli-platform-ios&lt;/span&gt;&lt;span class="se"&gt;\/&lt;/span&gt;&lt;span class="sr"&gt;.*$/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="sr"&gt;/^&lt;/span&gt;&lt;span class="se"&gt;((?!&lt;/span&gt;&lt;span class="sr"&gt;mobile&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="sr"&gt;.&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="sr"&gt;*&lt;/span&gt;&lt;span class="se"&gt;\/&lt;/span&gt;&lt;span class="sr"&gt;node_modules&lt;/span&gt;&lt;span class="se"&gt;\/&lt;/span&gt;&lt;span class="sr"&gt;@react-native-community&lt;/span&gt;&lt;span class="se"&gt;\/&lt;/span&gt;&lt;span class="sr"&gt;cli-platform-android&lt;/span&gt;&lt;span class="se"&gt;\/&lt;/span&gt;&lt;span class="sr"&gt;.*$/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="sr"&gt;/^&lt;/span&gt;&lt;span class="se"&gt;((?!&lt;/span&gt;&lt;span class="sr"&gt;mobile&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="sr"&gt;.&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="sr"&gt;*&lt;/span&gt;&lt;span class="se"&gt;\/&lt;/span&gt;&lt;span class="sr"&gt;node_modules&lt;/span&gt;&lt;span class="se"&gt;\/&lt;/span&gt;&lt;span class="sr"&gt;hermes-engine&lt;/span&gt;&lt;span class="se"&gt;\/&lt;/span&gt;&lt;span class="sr"&gt;.*$/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="sr"&gt;/^&lt;/span&gt;&lt;span class="se"&gt;((?!&lt;/span&gt;&lt;span class="sr"&gt;mobile&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="sr"&gt;.&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="sr"&gt;*&lt;/span&gt;&lt;span class="se"&gt;\/&lt;/span&gt;&lt;span class="sr"&gt;node_modules&lt;/span&gt;&lt;span class="se"&gt;\/&lt;/span&gt;&lt;span class="sr"&gt;jsc-android&lt;/span&gt;&lt;span class="se"&gt;\/&lt;/span&gt;&lt;span class="sr"&gt;.*$/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="sr"&gt;/^&lt;/span&gt;&lt;span class="se"&gt;((?!&lt;/span&gt;&lt;span class="sr"&gt;mobile&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="sr"&gt;.&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="sr"&gt;*&lt;/span&gt;&lt;span class="se"&gt;\/&lt;/span&gt;&lt;span class="sr"&gt;node_modules&lt;/span&gt;&lt;span class="se"&gt;\/&lt;/span&gt;&lt;span class="sr"&gt;react&lt;/span&gt;&lt;span class="se"&gt;\/&lt;/span&gt;&lt;span class="sr"&gt;.*$/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="sr"&gt;/^&lt;/span&gt;&lt;span class="se"&gt;((?!&lt;/span&gt;&lt;span class="sr"&gt;mobile&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="sr"&gt;.&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="sr"&gt;*&lt;/span&gt;&lt;span class="se"&gt;\/&lt;/span&gt;&lt;span class="sr"&gt;node_modules&lt;/span&gt;&lt;span class="se"&gt;\/&lt;/span&gt;&lt;span class="sr"&gt;react-native&lt;/span&gt;&lt;span class="se"&gt;\/&lt;/span&gt;&lt;span class="sr"&gt;.*$/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="sr"&gt;/^&lt;/span&gt;&lt;span class="se"&gt;((?!&lt;/span&gt;&lt;span class="sr"&gt;mobile&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="sr"&gt;.&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="sr"&gt;*&lt;/span&gt;&lt;span class="se"&gt;\/&lt;/span&gt;&lt;span class="sr"&gt;node_modules&lt;/span&gt;&lt;span class="se"&gt;\/&lt;/span&gt;&lt;span class="sr"&gt;react-native-codegen&lt;/span&gt;&lt;span class="se"&gt;\/&lt;/span&gt;&lt;span class="sr"&gt;.*$/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;]),&lt;/span&gt;
    &lt;span class="na"&gt;extraNodeModules&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@react-native-community/cli-platform-ios&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="s2"&gt;/Users/me/my-app/packages/mobile/node_modules/@react-native-community/cli-platform-ios&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="s2"&gt;@react-native-community/cli-platform-android&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="s2"&gt;/Users/me/my-app/packages/mobile/node_modules/@react-native-community/cli-platform-android&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="s2"&gt;hermes-engine&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="s2"&gt;/Users/me/my-app/packages/mobile/node_modules/hermes-engine&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="s2"&gt;jsc-android&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="s2"&gt;/Users/me/my-app/packages/mobile/node_modules/jsc-android&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;react&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/Users/me/my-app/packages/mobile/node_modules/react&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="s2"&gt;react-native&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="s2"&gt;/Users/me/my-app/packages/mobile/node_modules/react-native&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="s2"&gt;react-native-codegen&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="s2"&gt;/Users/me/my-app/packages/mobile/node_modules/react-native-codegen&lt;/span&gt;&lt;span class="dl"&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;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should finally be able to to run your app on iOS now:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmmazzarolo.com%2Fstatic%2F2ef8c3c2b7d3e2b2b6015eef23bc7dba%2F77672%2Fios.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmmazzarolo.com%2Fstatic%2F2ef8c3c2b7d3e2b2b6015eef23bc7dba%2F77672%2Fios.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Fixing the Android assets resolution bug
&lt;/h2&gt;

&lt;p&gt;If you run your app on Android, you'll notice that images won't be loaded correctly:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmmazzarolo.com%2Fstatic%2F101c006738d891c77b9abb9e4524fda8%2F78958%2Fandroid-ko.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmmazzarolo.com%2Fstatic%2F101c006738d891c77b9abb9e4524fda8%2F78958%2Fandroid-ko.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is because of &lt;a href="https://github.com/facebook/metro/issues/290" rel="noopener noreferrer"&gt;an open issue with the metro bundler logic used to load assets outside of the root directory on android&lt;/a&gt; (like our &lt;code&gt;app/src/logo.png&lt;/code&gt; image).&lt;/p&gt;

&lt;p&gt;To fix this issue, we can patch the metro bundler assets resolution mechanism by adding a custom server middleware in the metro config.&lt;br&gt;&lt;br&gt;
The way the fix works &lt;a href="https://github.com/facebook/metro/issues/290#issuecomment-543746458" rel="noopener noreferrer"&gt;is quite weird&lt;/a&gt;, but since it's available in &lt;code&gt;react-native-monorepo-tools&lt;/code&gt; you shouldn't have to worry too much about it.&lt;/p&gt;

&lt;p&gt;You can add it to metro the metro config this way:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt; const path = require("path");
 const exclusionList = require("metro-config/src/defaults/exclusionList");
 const {
   getMetroConfig,
   getAndroidAssetsResolutionFix,
 } = require("react-native-monorepo-tools");
&lt;span class="err"&gt;
&lt;/span&gt; const yarnWorkspacesMetroConfig = getMetroConfig();
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gi"&gt;+const androidAssetsResolutionFix = getMetroAndroidAssetsResolutionFix();
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt; module.exports = {
   transformer: {
     getTransformOptions: async () =&amp;gt; ({
&lt;span class="gi"&gt;+      // Apply the Android assets resolution fix to the public path...
+      publicPath: androidAssetsResolutionFix.publicPath,
+      transform: {
+        experimentalImportSupport: false,
+        inlineRequires: false,
+      },
+    }),
&lt;/span&gt;   },
&lt;span class="gi"&gt;+  server: {
+    // ...and to the server middleware.
+    enhanceMiddleware: (middleware) =&amp;gt; {
+      return androidAssetsResolutionFix.applyMiddleware(middleware);
+    },
+  },
&lt;/span&gt;   // Add additional Yarn workspace package roots to the module map.
   // This allows importing importing from all the project's packages.
   watchFolders: yarnWorkspacesMetroConfig.watchFolders,
   resolver: {
     // Ensure we resolve nohoist libraries from this directory.
     blockList: exclusionList(yarnWorkspacesMetroConfig.blockList),
     extraNodeModules: yarnWorkspacesMetroConfig.extraNodeModules,
   },
 };
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Try running Android — it should work correctly now 👍&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmmazzarolo.com%2Fstatic%2F4a339c974b8b553b6afcc315ecd24b40%2F78958%2Fandroid-ok.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmmazzarolo.com%2Fstatic%2F4a339c974b8b553b6afcc315ecd24b40%2F78958%2Fandroid-ok.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Developing and updating the app
&lt;/h2&gt;

&lt;p&gt;By using &lt;code&gt;react-native-monorepo-tools&lt;/code&gt; in the metro bundler configuration, we are consolidating all our Yarn workspaces settings into the root &lt;code&gt;package.json&lt;/code&gt;'s &lt;code&gt;nohoist&lt;/code&gt; list.&lt;br&gt;&lt;br&gt;
Whenever we need to add a new library that doesn't work well when hoisted (e.g., a native library), we can add it to the &lt;code&gt;nohoist&lt;/code&gt; list and run &lt;code&gt;yarn&lt;/code&gt; again so that the metro config can automatically pick up the updated settings.&lt;/p&gt;

&lt;p&gt;Additionally, since we haven't touched the native code, updating to newer versions of React Native shouldn't be an issue (as long as there aren't breaking changes in metro bundler).&lt;/p&gt;
&lt;h2&gt;
  
  
  Root-level scripts
&lt;/h2&gt;

&lt;p&gt;To improve a bit the developer experience, I recommend adding a few scripts to the top-level &lt;code&gt;package.json&lt;/code&gt; to invoke workspace-specific scripts (to avoid having to &lt;code&gt;cd&lt;/code&gt; into a directory every time you need to run a script).&lt;/p&gt;

&lt;p&gt;For example, you can add the following scripts to the mobile workspace:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"android"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"react-native run-android"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ios"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"react-native run-ios"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"start"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"react-native start"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"studio"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"studio android"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"xcode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"xed ios"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And then you can reference them from the root this way:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"android:metro"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"yarn workspace @my-app/mobile start"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"android:start"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"yarn workspace @my-app/mobile android"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"android:studio"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"yarn workspace @my-app/mobile studio"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ios:metro"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"yarn workspace @my-app/mobile start"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ios:start"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"yarn workspace @my-app/mobile ios"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ios:xcode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"yarn workspace @my-app/mobile xcode"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This pattern allows us to run workspace-specific script directly from the root directory.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next steps
&lt;/h2&gt;

&lt;p&gt;In the next step, we'll add support for Windows and macOS to our monorepo.&lt;/p&gt;

&lt;p&gt;Stay tuned!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/mmazzarolo/running-react-native-everywhere-4gpp"&gt;Overview&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/mmazzarolo/running-react-native-everywhere-yarn-workspaces-monorepo-3j5g"&gt;Monorepo setup&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Android &amp;amp; iOS (☜ you're here)&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/mmazzarolo/running-react-native-everywhere-windows-macos-7lp"&gt;Windows &amp;amp; macOS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/mmazzarolo/running-react-native-everywhere-the-web-h6d"&gt;The Web&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/mmazzarolo/running-react-native-everywhere-browser-extensions-electron-46hh"&gt;Electron &amp;amp; browser extension&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://mmazzarolo.com/blog/2021-09-18-running-react-native-everywhere-mobile/" rel="noopener noreferrer"&gt;mmazzarolo.com&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>react</category>
      <category>reactnative</category>
    </item>
    <item>
      <title>Running React Native everywhere: Yarn Workspaces monorepo</title>
      <dc:creator>Matteo Mazzarolo</dc:creator>
      <pubDate>Thu, 16 Sep 2021 13:14:00 +0000</pubDate>
      <link>https://dev.to/mmazzarolo/running-react-native-everywhere-yarn-workspaces-monorepo-3j5g</link>
      <guid>https://dev.to/mmazzarolo/running-react-native-everywhere-yarn-workspaces-monorepo-3j5g</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;First part of the &lt;a href="https://dev.to/mmazzarolo/running-react-native-everywhere-4gpp"&gt;"Running React Native everywhere"&lt;/a&gt; series.&lt;br&gt;&lt;br&gt;
In this post, we'll lay the foundation for running multiple React Native apps within a single codebase.&lt;/p&gt;

&lt;p&gt;Highlights:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Using a monorepo to support multiple platforms with React Native&lt;/li&gt;
&lt;li&gt;What are Yarn Workspaces &lt;code&gt;nohoist&lt;/code&gt;'s benefits&lt;/li&gt;
&lt;li&gt;Bootstrapping a minimal Yarn Workspaces setup&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Multi-platform support
&lt;/h2&gt;

&lt;p&gt;Running React Native on multiple platforms is not a new thing. We've been able to run React Native on the web, macOS, and Windows for quite a while now.&lt;br&gt;&lt;br&gt;
The most common and straightforward way to support different platforms with a single React Native codebase is to store all the configuration files required to run the app on all platforms in a single project directory.&lt;/p&gt;

&lt;p&gt;For example, if you're planning to support Android, iOS, Windows, and macOS, by following the &lt;a href="https://microsoft.github.io/react-native-windows/docs/getting-started"&gt;React Native for Windows + macOS&lt;/a&gt; documentation, you'll end up with a project that looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
└── &amp;lt;project-root&amp;gt;/
    ├── android/
    ├── ios/
    ├── macos/
    ├── src/
    ├── windows/
    ├── app.json
    ├── babel.config.js
    ├── index.js
    ├── metro.config.js
    └── package.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This structure &lt;strong&gt;can&lt;/strong&gt; work perfectly fine for most use cases.&lt;/p&gt;

&lt;p&gt;...but, from my personal experience, it has a few drawbacks that get exponentially worse the more your codebase grows.&lt;/p&gt;

&lt;p&gt;First and foremost: &lt;strong&gt;you're forced to use the same version of React Native on every platform you support&lt;/strong&gt;.&lt;br&gt;&lt;br&gt;
Therefore, you won't be able to update React Native until all platforms are ready to support it.&lt;br&gt;&lt;br&gt;
Even though this limitation may not seem like an issue at first, it might get you stuck on older versions of React Native if even a single platform is not compatible with the latest versions.&lt;br&gt;&lt;br&gt;
Let's look at a real case example: as of today (Sep 2021), the latest stable version for React Native for macOS supports only React Native &lt;code&gt;0.63.4&lt;/code&gt; (released in Oct 2020).&lt;br&gt;&lt;br&gt;
Assuming we're planning to support both Android/iOS and macOS, we won't be able to update React Native in our project until React Native for macOS supports it. And we'd be stuck on an (almost) 1-year-old version of React Native even on Android/iOS.&lt;br&gt;&lt;br&gt;
&lt;em&gt;P.S.: To be clear, I'm not criticizing React Native for macOS's release cycle. It's just the first example of versions gap that comes to my mind.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Second, &lt;strong&gt;sharing code with other projects&lt;/strong&gt; (e.g., backend code, web apps) &lt;strong&gt;may get complicated&lt;/strong&gt;.&lt;br&gt;&lt;br&gt;
Out-of-the-box, &lt;a href="https://facebook.github.io/metro/"&gt;React Native's metro bundler&lt;/a&gt; cannot reference code outside of the project's root directory. You &lt;em&gt;can&lt;/em&gt; configure it to do so (and we'll do it as well later on). Still, once you do it, you'll also need to ensure dependencies resolution works correctly (to avoid loading two different versions of the same library, for example); which might not be as easy as it may sound.&lt;/p&gt;

&lt;p&gt;Last, because you're supporting multiple platforms in a single directory, &lt;strong&gt;it's easy to end up with confusing indirections and branches in platform-specific files&lt;/strong&gt;.&lt;br&gt;&lt;br&gt;
This may be just a "me" thing, but I find it hard to navigate around configuration files of projects that support multiple platforms. At first glance, it may look like all platforms use the same configuration files. But once you dig a bit deeper, you realize that each platform requires some ad-hoc tweaks to the configuration files (for Metro, Babel, Webpack, etc.).&lt;br&gt;&lt;br&gt;
Want an example from a codebase I wrote?&lt;br&gt;&lt;br&gt;
Check out &lt;a href="https://github.com/mmazzarolo/ordinary-puzzles-app"&gt;Ordinary Puzzles&lt;/a&gt;, which is a mobile, web, and Electron app.&lt;br&gt;&lt;br&gt;
It's not easy to understand what files are used by which platform (e.g., what platform build phase is using &lt;code&gt;babel.config.js&lt;/code&gt;?)&lt;/p&gt;
&lt;h2&gt;
  
  
  A possible answer to these issues: Yarn Workspaces monorepo
&lt;/h2&gt;

&lt;p&gt;A possible way to solve these issues that I've been using with success for a while now (and the one we'll use in this guide) is structuring the project as a &lt;a href="https://classic.yarnpkg.com/en/docs/workspaces/"&gt;Yarn Workspaces&lt;/a&gt; monorepo, keeping platform-specific code in different packages.&lt;/p&gt;

&lt;p&gt;Yarn Workspaces (and monorepos in general) is a way to allow multiple apps to coexist in the same repository and cross-reference each other, easing the overhead of repository management and allowing a higher degree of collaboration among teams.&lt;br&gt;&lt;br&gt;
Each app is known as "package", and has its own &lt;code&gt;package.json&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;Thanks to Yarn Workspaces, &lt;strong&gt;we can go from a single app that runs on different platforms&lt;/strong&gt; to &lt;strong&gt;multiple apps that share common JavaScript code&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
└── &amp;lt;project-root&amp;gt;/
    &lt;span class="c"&gt;# JavaScript code of the app (shared between all apps)&lt;/span&gt;
    ├── app/
    │   ├── src/
    │   └── package.json
    &lt;span class="c"&gt;# macOS app configuration files and native code&lt;/span&gt;
    ├── macos/
    │   ├── macos/
    │   ├── app.json
    │   ├── babel.config.js
    │   ├── index.js
    │   ├── metro.config.js
    │   └── package.json
    &lt;span class="c"&gt;# Android/iOS app configuration files and native code&lt;/span&gt;
    ├── mobile/
    │   ├── android/
    │   ├── ios/
    │   ├── app.json
    │   ├── babel.config.js
    │   ├── index.js
    │   ├── metro.config.js
    │   └── package.json
    &lt;span class="c"&gt;# Windows app configuration files and native code&lt;/span&gt;
    └── windows/
        ├── windows/
        ├── app.json
        ├── babel.config.js
        ├── index.js
        ├── metro.config.js
        └── package.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To me, this structure perfectly suits &lt;strong&gt;React Native's "Learn once, write anywhere" headline&lt;/strong&gt;.&lt;br&gt;&lt;br&gt;
By using a single project structure, it's easy to forget that we're not developing a "single" app: we're developing different apps (Android, iOS, web, etc.) that run the same JavaScript code.&lt;br&gt;&lt;br&gt;
The difference between a monolithic approach and monorepo, is that in the former, all apps are forced to use the same dependency versions. In the latter, you're free to use different dependency versions on each app.&lt;/p&gt;

&lt;p&gt;This "freedom" comes as a double-edged sword.&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Suppose you decide to use two different versions of React Native. In that case, the shared JavaScript code must support both versions&lt;/strong&gt;: you can't simply run the current version of React Native on a platform and a two year old version of it on another and expect the shared JavaScript code to just work. Even if React Native is steadily becoming more "stable" you still need to take into account breaking changes.&lt;/p&gt;

&lt;p&gt;That said, as we'll see later on, between platform-specific file names (&lt;code&gt;index.ios.js&lt;/code&gt;, &lt;code&gt;index.web.js&lt;/code&gt;, etc.) and being able to isolate platform-specific JavaScript code in packages, supporting different dependency versions might be easier than you expect.&lt;/p&gt;
&lt;h2&gt;
  
  
  Yarn's nohoist
&lt;/h2&gt;

&lt;p&gt;A crucial part of our monorepo setup is &lt;a href="https://classic.yarnpkg.com/blog/2018/02/15/nohoist/"&gt;Yarn's &lt;code&gt;nohoist&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To reduce redundancy, most package managers employ some kind of hoisting scheme to extract and flatten all dependent modules, as much as possible, into a centralized location.&lt;br&gt;
Yarn Workspaces store the flattened dependencies in a &lt;code&gt;node_modules&lt;/code&gt; directory in the project root and makes it accessible to the workspace packages by symlinking the libraries in the packages' &lt;code&gt;node_module&lt;/code&gt; directory.&lt;/p&gt;

&lt;p&gt;While it might appear that we can access all modules from the project’s root &lt;code&gt;node_modules&lt;/code&gt;, the reality is that build processes sometimes aren't able to traverse symlinks.&lt;br&gt;
This problem is especially prominent in React Native apps, where both the &lt;a href="https://github.com/facebook/metro/issues/1"&gt;metro bundler&lt;/a&gt; and the native code can't follow symlinks.&lt;/p&gt;

&lt;p&gt;A common way to solve this issue in React Native monorepos, is to configure the metro bundler and the native layer to use the project's root &lt;code&gt;node_modules&lt;/code&gt; directory instead of the package's one.&lt;br&gt;
While this approach ensures you gain all the benefits of the hoisting process, it introduces a few complexities:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Each time you update React Native (or a library that requires native linking), you must also update (or at least keep in sync) the native code with the root project's &lt;code&gt;node_modules&lt;/code&gt; directory. To me, this process has always seemed error-prone, because you're dealing with multiple languages and build-tools.&lt;/li&gt;
&lt;li&gt;Suppose your packages need different versions of React Native (or of a library that requires native linking). In that case, you can't ensure React Native will be installed in a specific location (unless you give up the hoisting mechanism) — adding even more complexities to the table.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For these reasons, we'll use a different approach: &lt;strong&gt;&lt;a href="https://classic.yarnpkg.com/blog/2018/02/15/nohoist/"&gt;Yarn's &lt;code&gt;nohoist&lt;/code&gt;&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Yarn's &lt;code&gt;nohoist&lt;/code&gt; is a setting that disables the selected modules from being hoisted to the project root.&lt;/strong&gt; They are placed in the actual (child) project instead, just like in a standalone, non-workspaces, project.&lt;/p&gt;

&lt;p&gt;Of course, this comes with drawbacks. The most obvious one is that nohoist modules could be duplicated in multiple locations, denying the benefit of hoisting mentioned above. Therefore, we'll keep nohoist scope as small and explicit as possible, targeting only problematic libraries.&lt;/p&gt;

&lt;p&gt;Thanks to nohoist, we can avoid making changes to the native code, and we can keep the monorepo configuration in the JavaScript land. This means we can even extract common metro and webpack settings in a workspace package to share them easily across the entire project.&lt;/p&gt;

&lt;p&gt;And, even more importantly, different platforms can use different versions of React Native (and native libraries), favoring incremental updates instead of migrating the entire project.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Please notice that I'm focusing on Yarn Workspaces just because it's the tool I'm most familiar with. You can achieve similar results with &lt;a href="https://pnpm.io/"&gt;pnpm&lt;/a&gt; and &lt;a href="https://nx.dev/"&gt;nx&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  Creating our monorepo
&lt;/h2&gt;

&lt;p&gt;Enough with the theory! Let's start the setup of our monorepo.&lt;/p&gt;

&lt;p&gt;First of all, ensure &lt;a href="https://classic.yarnpkg.com/lang/en/"&gt;Yarn 1 (classic)&lt;/a&gt; is installed.&lt;/p&gt;

&lt;p&gt;Then, initialize a new &lt;code&gt;my-app&lt;/code&gt; project&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Create the project dir and cd into it.&lt;/span&gt;
&lt;span class="nb"&gt;mkdir &lt;/span&gt;my-app &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;my-app

&lt;span class="c"&gt;# Initialize git.&lt;/span&gt;
git init
npx gitignore node
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add this &lt;code&gt;package.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"my-app"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0.0.1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"private"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"workspaces"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"packages"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"packages/*"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"reset"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"find . -type dir -name node_modules | xargs rm -rf &amp;amp;&amp;amp; rm -rf yarn.lock"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;workspaces.packages&lt;/code&gt; setting tells Yarn that each package (e.g., &lt;code&gt;mobile&lt;/code&gt;, &lt;code&gt;macos&lt;/code&gt;, etc.) will live in &lt;code&gt;&amp;lt;root&amp;gt;/packages/&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;reset&lt;/code&gt; script deletes all the &lt;code&gt;node_modules&lt;/code&gt; directories in the project (recursively) and the root &lt;code&gt;yarn.lock&lt;/code&gt; file. It may come in handy during the initial phase of the setup if we mistakenly install dependencies that should be nohoisted before adding them to &lt;code&gt;nohoist&lt;/code&gt; :)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Create an empty &lt;code&gt;packages&lt;/code&gt; directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;packages
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, the most important part: add a &lt;code&gt;nohoist&lt;/code&gt; section to your &lt;code&gt;package.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"my-app"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0.0.1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"private"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"workspaces"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"packages"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"packages/*"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"nohoist"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"**/react"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"**/react-dom"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"**/react-native"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"reset"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"find . -type dir -name node_modules | xargs rm -rf &amp;amp;&amp;amp; rm -rf yarn.lock"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This &lt;code&gt;nohoist&lt;/code&gt; section will tell Yarn that the listed dependencies (specified as &lt;a href="https://github.com/isaacs/minimatch"&gt;glob patterns&lt;/a&gt;) should be installed in the &lt;code&gt;node_modules&lt;/code&gt; directory of each package instead of the root project's one.&lt;br&gt;&lt;br&gt;
For now, I just added &lt;code&gt;react&lt;/code&gt;, &lt;code&gt;react-dom&lt;/code&gt;, and &lt;code&gt;react-native&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;react-native&lt;/code&gt; because the native code requires/expects it to be installed at the package level.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;react&lt;/code&gt; and &lt;code&gt;react-dom&lt;/code&gt; because once we start supporting the React Native for Web, it will be easy to get into a state where the app crashes because &lt;a href="https://github.com/facebook/react/issues/13991"&gt;different versions of React are loaded on the page&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Spoiler: we'll have to come back and update this list every time we notice a dependency doesn't work well when hoisted.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We're done, for now!&lt;/p&gt;

&lt;h2&gt;
  
  
  Next steps
&lt;/h2&gt;

&lt;p&gt;In the next step, we'll add support for Android and iOS to our monorepo and learn how to configure the metro bundler dynamically based on the &lt;code&gt;nohoist&lt;/code&gt; list.&lt;/p&gt;

&lt;p&gt;Stay tuned!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/mmazzarolo/running-react-native-everywhere-4gpp"&gt;Overview&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Monorepo setup (☜ you're here)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/mmazzarolo/running-react-native-everywhere-android-ios-3451"&gt;Android &amp;amp; iOS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/mmazzarolo/running-react-native-everywhere-windows-macos-7lp"&gt;Windows &amp;amp; macOS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/mmazzarolo/running-react-native-everywhere-the-web-h6d"&gt;The Web&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/mmazzarolo/running-react-native-everywhere-browser-extensions-electron-46hh"&gt;Electron &amp;amp; browser extension&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://mmazzarolo.com/blog/2021-09-12-running-react-native-everywhere-monorepo/"&gt;mmazzarolo.com&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://mmazzarolo.com/blog/2021-09-12-running-react-native-everywhere-monorepo/"&gt;mmazzarolo.com&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>react</category>
      <category>reactnative</category>
    </item>
    <item>
      <title>Running React Native everywhere</title>
      <dc:creator>Matteo Mazzarolo</dc:creator>
      <pubDate>Thu, 16 Sep 2021 11:20:00 +0000</pubDate>
      <link>https://dev.to/mmazzarolo/running-react-native-everywhere-4gpp</link>
      <guid>https://dev.to/mmazzarolo/running-react-native-everywhere-4gpp</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;How to structure your codebase to run multiple React Native apps within a single codebase targeting different platforms: Android, iOS, macOS, Windows, the web, a browser extension, and Electron.&lt;/p&gt;

&lt;p&gt;The complete project is available on GitHub: &lt;a href="https://github.com/mmazzarolo/react-native-universal-monorepo" rel="noopener noreferrer"&gt;React Native Universal Monorepo&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fmmazzarolo%2Freact-native-universal-monorepo%2Fmaster%2F.github%2Fimages%2Fall-screenshot.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fmmazzarolo%2Freact-native-universal-monorepo%2Fmaster%2F.github%2Fimages%2Fall-screenshot.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Learn once, run everywhere
&lt;/h2&gt;

&lt;p&gt;Since its inception, &lt;a href="https://reactnative.dev/" rel="noopener noreferrer"&gt;React Native&lt;/a&gt;'s focus has always been enabling developers to write native applications using React. And by "native", they don't mean just "mobile" apps.&lt;br&gt;&lt;br&gt;
Yes, React Native is mainly known for its Android and iOS support, but its scope is steadily expanding: be it mobile devices, desktop apps, VR, or websites, React Native has an answer to almost every platform nowadays.&lt;/p&gt;

&lt;p&gt;"Running React Native everywhere" is an in-depth guide about &lt;strong&gt;using React Native to run a single app on different platforms and frameworks: Android, iOS, macOS, Windows, the web, a browser extension, and Electron&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This guide leans towards showing the technical side of the platform's integration process, focusing on creating a setup that provides a good developer experience.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;This is not a "how to create a React Native app" guide&lt;/strong&gt;. We'll spend most of the time getting our hands dirty by tweaking &lt;a href="https://facebook.github.io/metro/" rel="noopener noreferrer"&gt;React Native's metro bundler&lt;/a&gt;, customizing &lt;a href="https://webpack.js.org/" rel="noopener noreferrer"&gt;Webpack configurations&lt;/a&gt;, and learning how monorepos and &lt;a href="https://classic.yarnpkg.com/en/docs/workspaces/" rel="noopener noreferrer"&gt;Yarn workspaces&lt;/a&gt; work.&lt;/p&gt;

&lt;p&gt;I won’t delve deeply into explaining how the technology stack works. A good understanding of React/React Native is required if you want to fully understand the process. Some basic knowledge of build tools like Webpack and the metro bundler will help too (but are not required).&lt;/p&gt;

&lt;p&gt;Last but not least: I'm not looking for buy-in on using React Native on multiple platforms. &lt;strong&gt;I'll assume you've already done some research on the pros and cons of such an approach&lt;/strong&gt; 👍.&lt;/p&gt;

&lt;h2&gt;
  
  
  Project goal
&lt;/h2&gt;

&lt;p&gt;By the end of this guide, we’ll have a fully working &lt;strong&gt;multi-platform setup sharing a single React Native codebase&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;You can find the complete code in the &lt;a href="https://github.com/mmazzarolo/react-native-universal-monorepo" rel="noopener noreferrer"&gt;React Native Universal Monorepo&lt;/a&gt; GitHub project.&lt;/p&gt;

&lt;p&gt;Even if I'm covering several platforms, you’re likely interested in just a couple of them.&lt;br&gt;&lt;br&gt;
That's ok, and that's why I'm splitting the guide into different blog posts — you can skip posts of platforms you don't care about.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tech stack
&lt;/h2&gt;

&lt;p&gt;The project we're building is structured as a &lt;a href="https://classic.yarnpkg.com/en/docs/workspaces/" rel="noopener noreferrer"&gt;&lt;strong&gt;Yarn workspaces&lt;/strong&gt;&lt;/a&gt; monorepo.&lt;/p&gt;

&lt;p&gt;For the sake of simplicity, code is written in &lt;strong&gt;plain JavaScript&lt;/strong&gt;. Still, you can add support for &lt;a href="https://www.typescriptlang.org/" rel="noopener noreferrer"&gt;&lt;strong&gt;TypeScript&lt;/strong&gt;&lt;/a&gt; if needed (you can use the &lt;a href="https://github.com/mmazzarolo/react-native-universal-monorepo" rel="noopener noreferrer"&gt;React Native Universal Monorepo&lt;/a&gt; as an example; it's written in TypeScript).&lt;/p&gt;

&lt;p&gt;We're not going to use &lt;strong&gt;Expo&lt;/strong&gt;. Not because I don't like it (quite the opposite: I love it!), but because currently it doesn't support all our target platforms.&lt;/p&gt;

&lt;p&gt;We'll generate the iOS and Android app using &lt;a href="https://reactnative.dev/docs/environment-setup" rel="noopener noreferrer"&gt;&lt;strong&gt;React Native CLI&lt;/strong&gt;&lt;/a&gt;, and the Windows and macOS apps app using &lt;a href="https://microsoft.github.io/react-native-windows/" rel="noopener noreferrer"&gt;&lt;strong&gt;React Native for Windows + macOS&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For the web app, the browser extension, and the Electron app, we'll use &lt;a href="https://necolas.github.io/react-native-web/" rel="noopener noreferrer"&gt;&lt;strong&gt;React Native for Web&lt;/strong&gt;&lt;/a&gt;.&lt;br&gt;&lt;br&gt;
To simplify their setup, we'll use &lt;a href="https://github.com/facebook/create-react-app" rel="noopener noreferrer"&gt;&lt;strong&gt;Create React App&lt;/strong&gt;&lt;/a&gt;, and customize it using &lt;a href="https://github.com/gsoft-inc/craco" rel="noopener noreferrer"&gt;&lt;strong&gt;CRACO&lt;/strong&gt;&lt;/a&gt; — but you're free to use other React-based frameworks as well (e.g., NextJS).&lt;/p&gt;

&lt;p&gt;As of writing, the latest version of React Native is 0.65. This is the version of React Native we're going to use on most of the codebase (except for the macOS app that currently only supports React Native 0.63).&lt;br&gt;&lt;br&gt;
Even if we're going to mess around with the configuration of each project, updating to newer versions of React Native &lt;em&gt;shouldn't&lt;/em&gt; be too complex. We'll keep all our configurations on the JavaScript side and, by design, we'll support and use different versions of React Native.&lt;/p&gt;

&lt;h2&gt;
  
  
  Acknowledgements
&lt;/h2&gt;

&lt;p&gt;I created &lt;a href="https://github.com/mmazzarolo/react-native-universal-monorepo" rel="noopener noreferrer"&gt;React Native Universal Monorepo&lt;/a&gt; and I'm writing these blog posts because I wanted to share what I learned while working on this setup on other personal projects.&lt;/p&gt;

&lt;p&gt;For feedback and questions, feel free to start a discussion on the &lt;a href="https://github.com/mmazzarolo/react-native-universal-monorepo/discussions" rel="noopener noreferrer"&gt;React Native Universal Monorepo's discussions page&lt;/a&gt; or send me a direct message.&lt;/p&gt;

&lt;p&gt;Thanks to the React + React Native team and community for building all these fantastic tools! ♥&lt;/p&gt;

&lt;h2&gt;
  
  
  Next steps
&lt;/h2&gt;

&lt;p&gt;Without further ado, let's start from the first step: &lt;a href="https://dev.to/mmazzarolo/running-react-native-everywhere-yarn-workspaces-monorepo-3j5g"&gt;setting up the monorepo&lt;/a&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Overview (☜ you're here)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/mmazzarolo/running-react-native-everywhere-yarn-workspaces-monorepo-3j5g"&gt;Monorepo setup&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/mmazzarolo/running-react-native-everywhere-android-ios-3451"&gt;Android &amp;amp; iOS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/mmazzarolo/running-react-native-everywhere-windows-macos-7lp"&gt;Windows &amp;amp; macOS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/mmazzarolo/running-react-native-everywhere-the-web-h6d"&gt;The Web&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/mmazzarolo/running-react-native-everywhere-browser-extensions-electron-46hh"&gt;Electron &amp;amp; browser extension&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://mmazzarolo.com/blog/2021-09-11-running-react-native-everywhere/" rel="noopener noreferrer"&gt;mmazzarolo.com&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>react</category>
      <category>reactnative</category>
    </item>
    <item>
      <title>Loading web workers using Webpack 5</title>
      <dc:creator>Matteo Mazzarolo</dc:creator>
      <pubDate>Mon, 06 Sep 2021 11:58:00 +0000</pubDate>
      <link>https://dev.to/mmazzarolo/loading-web-workers-using-webpack-5-4gcj</link>
      <guid>https://dev.to/mmazzarolo/loading-web-workers-using-webpack-5-4gcj</guid>
      <description>&lt;p&gt;Just wanted to share a few notes around the currently available options for loading web workers using webpack 5.&lt;/p&gt;

&lt;h2&gt;
  
  
  Web Workers overview
&lt;/h2&gt;

&lt;p&gt;Web workers allow you to push work outside of main execution thread of JavaScript, making them convenient for lengthy computations and background work.&lt;/p&gt;

&lt;p&gt;Web workers are delivered as scripts that are loaded asynchronously using the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Worker"&gt;Web Worker API&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;A worker is an object created using a constructor (e.g., &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Worker/Worker"&gt;&lt;code&gt;Worker()&lt;/code&gt;&lt;/a&gt;) that runs a named JavaScript file.&lt;/p&gt;

&lt;p&gt;To create a new worker, all you need to do is call the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Worker/Worker"&gt;&lt;code&gt;Worker()&lt;/code&gt;&lt;/a&gt; constructor, specifying the URI of a script to execute:&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="c1"&gt;// Assuming we're in a JavaScript script that runs in your main thread and that&lt;/span&gt;
&lt;span class="c1"&gt;// the worker script is available at yourdomain.com/worker.js, this will take&lt;/span&gt;
&lt;span class="c1"&gt;// care of spawning a new worker:&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;myWorker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Worker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;worker.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;Since they’re loaded as separate scripts, web workers can’t be “bundled” within the code that runs in the main thread. This means that if you’re using a module bundler to bundle your code (e.g., Webpack, Rollup) you’ll have to maintain two separate build processes, which can be pretty annoying.&lt;/p&gt;

&lt;p&gt;The good news is that, if you’re using webpack, there are a couple of tools you can use to simplify the loading process of web workers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Web Workers in webpack 5
&lt;/h2&gt;

&lt;p&gt;Since webpack 5, &lt;a href="https://webpack.js.org/guides/web-workers/"&gt;web workers are first-class citizens&lt;/a&gt;, and you can use a specific syntax to let webpack automatically handle the creation of two separate bundles.&lt;br&gt;&lt;br&gt;
To do so, you must use the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import.meta"&gt;&lt;code&gt;import.meta&lt;/code&gt; object&lt;/a&gt; (an object that exposes context-specific metadata) to provide the module URL to the &lt;code&gt;Worker()&lt;/code&gt; constructor:&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;const&lt;/span&gt; &lt;span class="nx"&gt;myWorker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Worker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./worker.js&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="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Note that the &lt;code&gt;import.meta.url&lt;/code&gt; construct is available only available in ESM. Worker in CommonJS syntax is not supported.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;As of today, there’s not much documentation around webpack 5’s web worker supports. It indeed works pretty well for the most common use-cases and it’s a future-proof way to load web worker, but, for now, if you’re looking for a more flexible way to load web workers, you might want to take a look at &lt;a href="https://github.com/webpack-contrib/worker-loader"&gt;&lt;code&gt;worker-loader&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Webpack 5 and Worker Loader
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/webpack-contrib/worker-loader"&gt;&lt;code&gt;worker-loader&lt;/code&gt;&lt;/a&gt; is the pre-webpack-5 way to load web workers, and its documentation highlights how it’s &lt;strong&gt;not&lt;/strong&gt; compatible with webpack 5 (&lt;em&gt;“Worker Loader is a loader for webpack 4”&lt;/em&gt;).&lt;br&gt;&lt;br&gt;
Still, in my experience, besides a few quirks, &lt;code&gt;worker-loader&lt;/code&gt; &lt;strong&gt;can&lt;/strong&gt; be used with webpack 5, and it offers &lt;a href="https://github.com/webpack-contrib/worker-loader#options"&gt;several more customization options&lt;/a&gt; than webpack 5’s built-in web worker support.&lt;/p&gt;

&lt;p&gt;The most important ones are probably the support for inlining web workers as &lt;code&gt;BLOB&lt;/code&gt; and specifying a custom &lt;code&gt;publicPath&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  Inlining web workers
&lt;/h3&gt;

&lt;p&gt;Web workers are restricted by a &lt;a href="https://en.wikipedia.org/wiki/Same-origin_policy"&gt;same-origin policy&lt;/a&gt;, so if your webpack assets are not being served from the same origin as your application, their download may be blocked by your browser.&lt;/p&gt;

&lt;p&gt;This scenario can commonly occur if you are serving the web worker from localhost during local development (e.g., with &lt;code&gt;webpack-dev-server&lt;/code&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="c1"&gt;// If the origin of your application is available at a different origin than&lt;/span&gt;
&lt;span class="c1"&gt;// localhost:8080, you won't be able to load the following web worker:&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;myWorker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Worker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;http://localhost:8080/worker.js&lt;/span&gt;&lt;span class="dl"&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;&lt;code&gt;worker-loader&lt;/code&gt; solves the local development issue by allowing you to inlining the web worker as a &lt;code&gt;BLOB&lt;/code&gt; (instead of pointing it to localhost) on development builds by specifying an &lt;code&gt;inline: "fallback"&lt;/code&gt; option:&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="c1"&gt;// webpack.config.js&lt;/span&gt;
&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;module&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;rules&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;span class="na"&gt;loader&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;worker-loader&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;inline&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;isDevelopment&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fallback&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="s2"&gt;no-fallback&lt;/span&gt;&lt;span class="dl"&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;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;h3&gt;
  
  
  Setting a worker-specific &lt;code&gt;publicPath&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Another scenario where the same-origin policy might need some accommodations is if you’re hosting your main bundle code on a static CDN.&lt;br&gt;&lt;br&gt;
In this case, you’re probably going to set the &lt;code&gt;publicPath&lt;/code&gt; of your webpack &lt;code&gt;output&lt;/code&gt; to the CDN domain (e.g., &lt;code&gt;https://my-static-cdn&lt;/code&gt;), so that all the assets will reference it in production. Unfortunately, this pattern doesn’t work well when using web workers because you can’t reference a web worker that is hosted on a CDN (because of the same-origin policy):&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="c1"&gt;// Since the origin of the application (e.g., https://example.com) is different&lt;/span&gt;
&lt;span class="c1"&gt;// from the CDN one, you won't be able to load the following web worker:&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;myWorker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Worker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://my-static-cdn/worker.js&lt;/span&gt;&lt;span class="dl"&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;What’s great about &lt;code&gt;worker-loader&lt;/code&gt;, is that you can solve this issue by setting a worker-specific &lt;code&gt;publicPath&lt;/code&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="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Set the publicPath of all assets generated by this webpack build to&lt;/span&gt;
    &lt;span class="c1"&gt;// https://my-static-cdn/.&lt;/span&gt;
    &lt;span class="na"&gt;publicPath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://my-static-cdn/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;module&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;rules&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;span class="na"&gt;loader&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;worker-loader&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="c1"&gt;// Overrides the publicPath just for the web worker, marking it as&lt;/span&gt;
        &lt;span class="c1"&gt;// available on the same origin used by the app (notice that this is&lt;/span&gt;
        &lt;span class="c1"&gt;// a relative path).&lt;/span&gt;
        &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;publicPath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/workers/&lt;/span&gt;&lt;span class="dl"&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;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;h4&gt;
  
  
  A note on setting worker-loader’s &lt;code&gt;publicPath&lt;/code&gt; with webpack 5
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://webpack.js.org/guides/public-path/#automatic-publicpath"&gt;Webpack 5 introduced a mechanism to detect the &lt;code&gt;publicPath&lt;/code&gt; that should be used automatically&lt;/a&gt;. Sadly, the new automatic detection seems to be incompatible with &lt;code&gt;worker-loader&lt;/code&gt;’s &lt;code&gt;publicPath&lt;/code&gt;… but there are a couple of (hacky) ways you can solve this issue ;)&lt;/p&gt;

&lt;p&gt;The first one is to by setting the &lt;code&gt;publicPath&lt;/code&gt; on the fly.&lt;br&gt;&lt;br&gt;
Webpack 5 exposes a global variable called &lt;code&gt;__webpack_public_path__&lt;/code&gt; that allows you to do that.&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="c1"&gt;// Updates the `publicPath` at runtime, overriding whatever was set in the&lt;/span&gt;
&lt;span class="c1"&gt;// webpack's `output` section.&lt;/span&gt;
&lt;span class="nx"&gt;__webpack_public_path__&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/workers/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;myWorker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Worker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/workers/worker.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Eventually, restore the `publicPath` to whatever was set in output.&lt;/span&gt;
&lt;span class="nx"&gt;__webpack_public_path__&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://my-static-cdn/&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;blockquote&gt;
&lt;p&gt;I recommend using webpack’s &lt;a href="https://webpack.js.org/plugins/define-plugin/"&gt;&lt;code&gt;DefinePlugin&lt;/code&gt;&lt;/a&gt; to pass the public path as environment variables instead of hardcoding them in the source code.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The other (even more hacky) option is to apply the following patch to &lt;code&gt;worker-loader&lt;/code&gt; (using &lt;a href="https://www.npmjs.com/package/patch-package"&gt;&lt;code&gt;patch-package&lt;/code&gt;&lt;/a&gt;, for example):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="err"&gt;#&lt;/span&gt; worker-loader+3.0.8.patch
&lt;span class="err"&gt;#&lt;/span&gt; Compatible only with worker-loader 3.0.8.
&lt;span class="gh"&gt;diff --git a/node_modules/worker-loader/dist/utils.js b/node_modules/worker-loader/dist/utils.js
index 5910165..2f2d16e 100644
&lt;/span&gt;&lt;span class="gd"&gt;-------- a/node_modules/worker-loader/dist/utils.js
&lt;/span&gt;&lt;span class="gi"&gt;+++ b/node_modules/worker-loader/dist/utils.js
&lt;/span&gt;&lt;span class="p"&gt;@@ -63,12 +63,14 @@&lt;/span&gt; function workerGenerator(loaderContext, workerFilename, workerSource, options) {
   const esModule = typeof options.esModule !== "undefined" ? options.esModule : true;
   const fnName = `${workerConstructor}_fn`;

+ const publicPath = options.publicPath ? `"${options.publicPath}"` : ' __webpack_public_path__';
&lt;span class="gi"&gt;+
&lt;/span&gt;   if (options.inline) {
     const InlineWorkerPath = (0, _loaderUtils.stringifyRequest)(loaderContext, `!!${require.resolve("./runtime/inline.js")}`);
     let fallbackWorkerPath;

     if (options.inline === "fallback") {
&lt;span class="gd"&gt;- fallbackWorkerPath = ` __webpack_public_path__ + ${JSON.stringify(workerFilename)}`;
&lt;/span&gt;&lt;span class="gi"&gt;+ fallbackWorkerPath = `${publicPath} + ${JSON.stringify(workerFilename)}`;
&lt;/span&gt;     }

     return `
&lt;span class="p"&gt;@@ -77,7 +79,7 @@&lt;/span&gt; ${esModule ? `import worker from ${InlineWorkerPath};` : `var worker = require($
 ${esModule ? "export default" : "module.exports ="} function ${fnName}() {\n return worker(${JSON.stringify(workerSource)}, ${JSON.stringify(workerConstructor)}, ${JSON.stringify(workerOptions)}, ${fallbackWorkerPath});\n}\n`;
   }

- return `${esModule ? "export default" : "module.exports ="} function ${fnName}() {\n return new ${workerConstructor}( __webpack_public_path__ + ${JSON.stringify(workerFilename)}${workerOptions ? `, ${JSON.stringify(workerOptions)}` : ""});\n}\n`;
&lt;span class="gi"&gt;+ return `${esModule ? "export default" : "module.exports ="} function ${fnName}() {\n return new ${workerConstructor}(${publicPath} + ${JSON.stringify(workerFilename)}${workerOptions ? `, ${JSON.stringify(workerOptions)}` : ""});\n}\n`;
&lt;/span&gt; } // Matches only the last occurrence of sourceMappingURL
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For more info, check this &lt;a href="https://github.com/webpack-contrib/worker-loader/issues/281"&gt;GitHub issue&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Originally published at &lt;a href="https://mmazzarolo.com/blog/2021-09-03-loading-web-workers-using-webpack-5/"&gt;mmazzarolo.com&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>development</category>
      <category>webpack</category>
    </item>
  </channel>
</rss>
