<?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>Forem: Cathal Mac Donnacha 🚀</title>
    <description>The latest articles on Forem by Cathal Mac Donnacha 🚀 (@cathalmacdonnacha).</description>
    <link>https://forem.com/cathalmacdonnacha</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%2F370196%2F37fed3b4-5e3c-4111-9212-24895128449c.jpg</url>
      <title>Forem: Cathal Mac Donnacha 🚀</title>
      <link>https://forem.com/cathalmacdonnacha</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/cathalmacdonnacha"/>
    <language>en</language>
    <item>
      <title>Route-based code splitting with React</title>
      <dc:creator>Cathal Mac Donnacha 🚀</dc:creator>
      <pubDate>Fri, 04 Aug 2023 13:31:28 +0000</pubDate>
      <link>https://forem.com/cathalmacdonnacha/route-based-code-splitting-with-react-4hm</link>
      <guid>https://forem.com/cathalmacdonnacha/route-based-code-splitting-with-react-4hm</guid>
      <description>&lt;p&gt;Code splitting is a technique used to optimize the loading performance of web apps by breaking down the bundled JavaScript files into smaller, more manageable chunks. By loading only the required code for a specific route or page, route-based code splitting significantly reduces the initial load time and improves the overall user experience.&lt;/p&gt;

&lt;p&gt;In this article, we will explain some aspects of how we can achieve route-based code splitting along with some code examples.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Route-based Code Splitting?
&lt;/h2&gt;

&lt;p&gt;When developing large-scale applications, loading all the JavaScript code upfront can lead to increased initial load times and negatively impact user experience. In contrast, route-based code splitting allows you to divide your application into smaller chunks based on different routes or features. Only the code relevant to the current route is loaded, resulting in faster loading times for the specific page and better overall application performance.&lt;/p&gt;

&lt;p&gt;By using route-based code splitting, you can prioritize the most critical code for each route, optimizing the initial loading experience and reducing the time to interactive (TTI).&lt;/p&gt;

&lt;h2&gt;
  
  
  What do we need?
&lt;/h2&gt;

&lt;p&gt;In order to actually implement route-based code splitting, we need to make use of two things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/import"&gt;Dynamic import&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://react.dev/reference/react/lazy"&gt;React.lazy()&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's take a look at these in a bit more detail.&lt;/p&gt;

&lt;h3&gt;
  
  
  Dynamic import
&lt;/h3&gt;

&lt;p&gt;Dynamic import is an &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/import"&gt;ECMAScript feature&lt;/a&gt; that allows us to import modules on the fly. This is really powerful and unless you're unlucky enough to have to support IE, it can be used in all &lt;a href="https://caniuse.com/es6-module-dynamic-import"&gt;major browsers&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here's what the syntax looks like:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;import('./path-to-my-module.js');&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;import&lt;/code&gt; will return a promise, so you would handle it just like any other promise within your app.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./carModule.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="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kr"&gt;module&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kr"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;startEngine&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;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&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;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Error loading carModule:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// or you can use async/await&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;carModule&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./carModule.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;carModule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;startEngine&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A great use case for this would be when you only make use of a heavy module in a specific part of your app.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt; &lt;span class="nx"&gt;onClick&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;onSortCarsClick&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;Sort&lt;/span&gt; &lt;span class="nx"&gt;cars&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt;&lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;onSortCarsClick&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Load in the heavy module&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;carModule&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./carModule.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;carModule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sortCars&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;h2&gt;
  
  
  React.lazy()
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;React.lazy()&lt;/code&gt; is a function in React that enables you to perform "lazy" or "on-demand" loading of components. It ensures that the component will only be loaded when it's actually rendered.&lt;/p&gt;

&lt;p&gt;Before &lt;code&gt;React.lazy()&lt;/code&gt; was introduced, you might have needed to set up a more complex build tooling configuration to achieve similar code splitting behavior. With &lt;code&gt;React.lazy()&lt;/code&gt;, this process is simplified and integrated directly into React's core.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;const MyLazyComponent = React.lazy(() =&amp;gt; import('./MyComponent'));&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Suspense
&lt;/h3&gt;

&lt;p&gt;Since the component is no longer statically imported, we need to display something while it's dynamically loading. For that, we use React's &lt;code&gt;&amp;lt;Suspense&amp;gt;&lt;/code&gt; boundary.&lt;/p&gt;

&lt;p&gt;In the example below, you'll see we are displaying some fallback UI while the component is being loaded.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Suspense&lt;/span&gt; &lt;span class="nx"&gt;fallback&lt;/span&gt;&lt;span class="o"&gt;=&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;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Loading&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;/div&amp;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;MyLazyComponent&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="sr"&gt;/Suspense&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Show me the code
&lt;/h2&gt;

&lt;p&gt;Here's a full example of how we can use a combination of dynamic imports, &lt;code&gt;React.lazy()&lt;/code&gt; and React Router to achieve route-based code splitting.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;lazy&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Suspense&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;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;BrowserRouter&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;Router&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Switch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Route&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Link&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-router-dom&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// These components will only be loaded when they're actually rendered.&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Home&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;lazy&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./components/Home&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;About&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;lazy&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./components/About&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;Contact&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;lazy&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./components/Contact&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;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;Router&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;nav&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;ul&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;li&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;Link&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="o"&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="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Home&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Link&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;/li&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;li&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;Link&lt;/span&gt; &lt;span class="nx"&gt;to&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&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;About&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Link&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;/li&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;li&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;Link&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/contact&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Contact&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Link&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;/li&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;/ul&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;/nav&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;Suspense&lt;/span&gt; &lt;span class="nx"&gt;fallback&lt;/span&gt;&lt;span class="o"&gt;=&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;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Loading&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;/div&amp;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;Switch&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;Route&lt;/span&gt; &lt;span class="nx"&gt;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;/about&lt;/span&gt;&lt;span class="dl"&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;About&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="sr"&gt;/Route&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;Route&lt;/span&gt; &lt;span class="nx"&gt;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;/contact&lt;/span&gt;&lt;span class="dl"&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;Contact&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="sr"&gt;/Route&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;Route&lt;/span&gt; &lt;span class="nx"&gt;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;/&lt;/span&gt;&lt;span class="dl"&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;Home&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="sr"&gt;/Route&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;/Switch&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;/Suspense&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;/Router&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="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;h2&gt;
  
  
  Code splitting using bundlers
&lt;/h2&gt;

&lt;p&gt;Modern bundlers have built-in support for code splitting to enable efficient loading of modules. What often happens is when a bundler comes across a dynamic import within your app, it automatically creates a separate chunk (javascript file) which can be loaded later. This way it's not bundled within your main bundle file, and hence improving the initial load time of your app.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final thoughts
&lt;/h2&gt;

&lt;p&gt;As you can see, code splitting has the potential to give us big performance improvements. However, it's important not to become too obsessed with it as like most performance-related features, it also adds complexity, so only use it where it makes sense. This is why it's often a great first step to only use it for routes, and take it from there.&lt;/p&gt;

&lt;h2&gt;
  
  
  Want to see more?
&lt;/h2&gt;

&lt;p&gt;I mainly write about real tech topics I face in my everyday life as a Frontend Developer. If this appeals to you then feel free to follow me on Twitter: &lt;a href="https://twitter.com/cmacdonnacha"&gt;https://twitter.com/cmacdonnacha&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Bye for now. 👋&lt;/p&gt;

</description>
      <category>react</category>
      <category>webdev</category>
      <category>javascript</category>
      <category>performance</category>
    </item>
    <item>
      <title>Mocking Error, Empty and Loading states with MSW</title>
      <dc:creator>Cathal Mac Donnacha 🚀</dc:creator>
      <pubDate>Tue, 06 Dec 2022 13:40:17 +0000</pubDate>
      <link>https://forem.com/cathalmacdonnacha/mocking-error-empty-and-loading-states-with-msw-1255</link>
      <guid>https://forem.com/cathalmacdonnacha/mocking-error-empty-and-loading-states-with-msw-1255</guid>
      <description>&lt;p&gt;One of the less exciting things about being a Frontend Developer is having to handle error, empty and loading states. It may not be the most fun thing to do, but it's necessary in order to give your users the best experience possible. Thankfully, &lt;a href="https://mswjs.io/" rel="noopener noreferrer"&gt;Mock Service Worker (MSW)&lt;/a&gt; makes this really simple, and in turn...kinda fun. 😀&lt;/p&gt;

&lt;p&gt;In this article, we take a look at how we can use MSW to mock these states for both local development and in our tests.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting started
&lt;/h2&gt;

&lt;p&gt;While mocking these states, you'll see that we make use of page URL query parameters. I find this to be a great pattern as it gives us the flexibility to change them on the fly using only the browser, which makes developing UIs to handle these scenarios a breeze!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FOBdtSs9.gif%2520align%3D" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FOBdtSs9.gif%2520align%3D" alt="Codesandbox browser" width="1118" height="435"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Mocking errors
&lt;/h2&gt;

&lt;p&gt;Here's an example of how you can mock a simple error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handlers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="nx"&gt;rest&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="s2"&gt;`/your/api/endpoint`&lt;/span&gt;&lt;span class="p"&gt;,&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="nx"&gt;ctx&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;// Check if the '?error=true' query param has been &lt;/span&gt;
    &lt;span class="c1"&gt;// included in the page url.&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pageParams&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;URLSearchParams&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;search&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;isError&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;pageParams&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="s1"&gt;error&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;true&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Example: http://localhost:3000/your_page_url?error=true&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;isError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// Query param was found, so return an error response.&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;res&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Oops! Something went terribly wrong.&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="c1"&gt;// Otherwise - return a 200 OK response.&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;res&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&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;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Some cool data&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, you have full control over what data MSW will return, depending on the query parameter included in the page URL.&lt;/p&gt;

&lt;p&gt;You can also include this same error scenario in your tests:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;MyPage&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="nf"&gt;afterEach&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;// Make sure to reset page url state after each test, otherwise &lt;/span&gt;
    &lt;span class="c1"&gt;// tests written after this one will still include the &lt;/span&gt;
    &lt;span class="c1"&gt;// `?error=true` param.&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;history&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replaceState&lt;/span&gt;&lt;span class="p"&gt;({},&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;/&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;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should display an error for some reason&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="c1"&gt;// Add a query param to the page url so that we get &lt;/span&gt;
    &lt;span class="c1"&gt;// an error response back.&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;history&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replaceState&lt;/span&gt;&lt;span class="p"&gt;({},&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;/?error=true&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;renderPage&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// Check that the error message was displayed.&lt;/span&gt;
    &lt;span class="nf"&gt;expect&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;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findByText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Oops! Something went terribly wrong.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;toBeInTheDocument&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;
  
  
  Suppressing intentional errors
&lt;/h3&gt;

&lt;p&gt;When testing network errors, you may notice some &lt;code&gt;console.error&lt;/code&gt; messages polluting the test output. If this is the case for you, and since these errors are expected, you can simply suppress them inside your test:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should display an error for some reason&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="c1"&gt;// Suppress console errors that we intentionally trigger in this test.&lt;/span&gt;
  &lt;span class="nx"&gt;jest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;spyOn&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;error&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;mockImplementation&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Mocking empty states
&lt;/h2&gt;

&lt;p&gt;This is similar to mocking errors, but you return an empty response instead:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;rest&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="s2"&gt;`/your/api/endpoint`&lt;/span&gt;&lt;span class="p"&gt;,&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="nx"&gt;ctx&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;pageParams&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;URLSearchParams&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;search&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;isEmpty&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;pageParams&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="s1"&gt;empty&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;true&lt;/span&gt;&lt;span class="dl"&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;isEmpty&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Example: http://localhost:3000/your_page_url?empty=true&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;res&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&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;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&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;h2&gt;
  
  
  Mocking loading states
&lt;/h2&gt;

&lt;p&gt;This is something I find extremely useful when developing loading components. Here, we apply an infinite delay to the response so that it never resolves:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;rest&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="s2"&gt;`/your/api/endpoint`&lt;/span&gt;&lt;span class="p"&gt;,&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="nx"&gt;ctx&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;pageParams&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;URLSearchParams&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;search&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;isEmpty&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;pageParams&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="s1"&gt;loading&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;true&lt;/span&gt;&lt;span class="dl"&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;isLoading&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Example http://localhost:3000/your_page_url?loading=true&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;res&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&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;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({}),&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;infinite&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Alternative mocking patterns
&lt;/h2&gt;

&lt;p&gt;Though the examples given above which make use of page URL query parameters are what I recommended, here are some other ways to simulate states:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;In tests, you could use &lt;a href="https://mswjs.io/docs/api/setup-server/use#permanent-override" rel="noopener noreferrer"&gt;runtime overrides&lt;/a&gt; so that the response will only occur inside your single test.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://mswjs.io/docs/recipes/mocking-error-responses#examples" rel="noopener noreferrer"&gt;Return an error&lt;/a&gt; depending on the payload sent. e.g A certain username.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Look up the request URL query parameters using &lt;a href="https://mswjs.io/docs/basics/response-resolver#conditional-response" rel="noopener noreferrer"&gt;conditional responses&lt;/a&gt;. This is similar to what we've used above, except you would get the query parameter from the request URL instead of the page URL.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Utility functions
&lt;/h2&gt;

&lt;p&gt;After using this pattern for a while, I decided to create some utility functions which I've found useful while creating handlers.&lt;/p&gt;

&lt;h3&gt;
  
  
  isQueryParamPresent()
&lt;/h3&gt;

&lt;p&gt;As you can see from the examples above, we have some code that checks if a page URL query parameter is present:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pageParams&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;URLSearchParams&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;search&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;isSomething&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;pageParams&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="s1"&gt;something&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;true&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;This can get quite repetitive when you have multiple query parameters to check for. Let's go ahead and improve this by creating a re-usable utility function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// utils.ts&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;isQueryParamPresent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;queryName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;queryValue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;true&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;searchParams&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;URLSearchParams&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;search&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;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="nx"&gt;queryName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;queryValue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// handlers.ts&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;isQueryParamPresent&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./utils&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;const&lt;/span&gt; &lt;span class="nx"&gt;handlers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="nx"&gt;rest&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="s2"&gt;`/your/api/endpoint`&lt;/span&gt;&lt;span class="p"&gt;,&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="nx"&gt;ctx&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="nf"&gt;isQueryParamPresent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;error&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="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;res&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Oops! Something went terribly wrong.&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="c1"&gt;// You can also check for different values (true is the default).&lt;/span&gt;
    &lt;span class="c1"&gt;// Example http://localhost:3000/your_page_url?animal=dog&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;isQueryParamPresent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;animal&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;dog&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="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;res&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&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;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dog&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Banjo&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;return&lt;/span&gt; &lt;span class="nf"&gt;res&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&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;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Some cool data&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  customResponse()
&lt;/h3&gt;

&lt;p&gt;As your project grows, so will your handlers. Having to deal with error, empty and loading scenarios for every single request handler will start to get quite verbose. Therefore, I take advantage of MSW's &lt;a href="https://mswjs.io/docs/recipes/custom-response-composition" rel="noopener noreferrer"&gt;custom response composition&lt;/a&gt; to automatically include these scenarios in each response. It will also add a realistic server delay to the response, mimicking a real-world app. This gives us some nice "built-in" features which are abstracted away from the handlers.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// utils.ts&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;compose&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ResponseTransformer&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;msw&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;customResponse&lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;transformers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ResponseTransformer&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="nf"&gt;isQueryParamPresent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;loading&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="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
       &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;infinite&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; 
       &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&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;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;isQueryParamPresent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;error&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="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; 
      &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Oops! Something went terribly wrong.&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;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;isQueryParamPresent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;empty&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="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&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;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&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;// Return the exact same response transforms which were passed in, &lt;/span&gt;
  &lt;span class="c1"&gt;// but also add a realistic server delay.&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;response&lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;transformers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// handlers.ts&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;customResponse&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./utils&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;const&lt;/span&gt; &lt;span class="nx"&gt;handlers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="nx"&gt;rest&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="s2"&gt;`/your/api/endpoint`&lt;/span&gt;&lt;span class="p"&gt;,&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="nx"&gt;ctx&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;// Now error, loading and empty states are handled &lt;/span&gt;
    &lt;span class="c1"&gt;// under the hood by customResponse(). Nifty!&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;customResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&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;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Some cool data&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;These utility functions have really improved the developer experience within our team as we no longer have to write the same error, loading and empty checks over and over. We also used to add delays to every single response individually, so not having to even think about that has been awesome!&lt;/p&gt;

&lt;h2&gt;
  
  
  Page vs request URL query parameters
&lt;/h2&gt;

&lt;p&gt;Another popular pattern is to use request URL query parameters to return the appropriate response. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;rest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/your/api/endpoint&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="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="nx"&gt;ctx&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;// Check if the 'userId' param exists in the request url.&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt; &lt;span class="o"&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;url&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="s1"&gt;userId&lt;/span&gt;&lt;span class="dl"&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;userId&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mr-bad-user-1234&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="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;res&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;403&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;errorMessage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;User not found&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;The above is fine when only using MSW for tests, but not so great when using it for local development in the browser.&lt;/p&gt;

&lt;p&gt;I prefer to use page URL query parameters for the following reasons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;We can achieve the same behaviour across both tests and local development as you will be re-using the same mock definitions.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We can easily change the UI by swapping out query parameters in the browser's URL address bar.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We can bookmark URLs to quickly display certain scenarios, without having to remember the query parameters.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Live code example
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://codesandbox.io/s/example-of-mocking-error-empty-and-loading-states-with-msw-nil5vs?file=/src/mocks/handlers.js" rel="noopener noreferrer"&gt;Here's a CodeSandbox app&lt;/a&gt; where I've added live examples for the scenarios shown above.&lt;/p&gt;

&lt;p&gt;You can also run &lt;a href="https://codesandbox.io/s/example-of-mocking-error-empty-and-loading-states-with-msw-nil5vs?file=/src/App.test.js" rel="noopener noreferrer"&gt;the tests&lt;/a&gt; by opening a new terminal within CodeSandbox and running the &lt;code&gt;yarn test&lt;/code&gt; command.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FRSII9Qz.gif%2520align%3D" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FRSII9Qz.gif%2520align%3D" alt="Codesandbox new terminal" width="800" height="259"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Upcoming beta
&lt;/h2&gt;

&lt;p&gt;It's worth mentioning that at the time of writing this article, there's a new and improved version of MSW on the horizon which is &lt;a href="https://github.com/mswjs/msw/discussions/1464" rel="noopener noreferrer"&gt;currently in beta&lt;/a&gt;. Though I look forward to trying it out and applying these patterns to it, I decided to reference the latest production version (&lt;code&gt;0.47.1&lt;/code&gt;) as it's what most folks will continue to use for the next while.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final thoughts
&lt;/h2&gt;

&lt;p&gt;That's about it! You can see just how useful MSW can be, not just for testing, but for local development too. At the end of the day, it's just Javascript, so there's nothing stopping you from returning whatever data you want depending on a query parameter. The sky is truly the limit!&lt;/p&gt;

&lt;p&gt;If you found this article helpful or have additional tips, please leave a comment below. 🙂&lt;/p&gt;

&lt;h2&gt;
  
  
  Want to see more?
&lt;/h2&gt;

&lt;p&gt;I mainly write about real tech topics I face in my everyday life as a Frontend Developer. If this appeals to you then feel free to follow me on Twitter: &lt;a href="https://twitter.com/cmacdonnacha" rel="noopener noreferrer"&gt;https://twitter.com/cmacdonnacha&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Bye for now. 👋&lt;/p&gt;

</description>
      <category>discuss</category>
    </item>
    <item>
      <title>Migrating from Create React App (CRA) to Vite</title>
      <dc:creator>Cathal Mac Donnacha 🚀</dc:creator>
      <pubDate>Mon, 22 Aug 2022 09:51:14 +0000</pubDate>
      <link>https://forem.com/cathalmacdonnacha/migrating-from-create-react-app-cra-to-vite-5416</link>
      <guid>https://forem.com/cathalmacdonnacha/migrating-from-create-react-app-cra-to-vite-5416</guid>
      <description>&lt;p&gt;I recently migrated a production app within my company from create-react-app (CRA) to Vite, and the results have been great so far!&lt;/p&gt;

&lt;p&gt;In this article, I go through all the steps I took as part of the migration, in the hope that it might help others who are going through the same process. &lt;/p&gt;

&lt;h2&gt;
  
  
  Why switch?
&lt;/h2&gt;

&lt;p&gt;I want to start by saying that I really like CRA, it's helped me to quickly set up and maintain lots of projects (personal and professional). However, here are some of the reasons why we ultimately decided to make the switch:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No dedicated maintainer.&lt;/li&gt;
&lt;li&gt;Slow to release. This will only cause more issues down the line as more features are added to React and Webpack.&lt;/li&gt;
&lt;li&gt;Increasing number of vulnerabilities causing github dependabot alerts which our security team require we fix, regardless of it being a build tool or not.&lt;/li&gt;
&lt;li&gt;Speed. This wasn't really an issue for me as I rarely restart my dev server and my CI pipeline handles the production build for me. In saying that, the app I'm migrating is quite small, so this may be a bigger deal for those with larger and more complex apps. I certainly wouldn't migrate for this reason alone, but I must admit, the speed improvements are quite impressive. &lt;/li&gt;
&lt;li&gt;Vite has matured a lot and the community has grown significantly compared to when I first created this CRA based app 2 years ago. If I was to evaluate both again for a new project, I would choose Vite this time around.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Given all of these, I thought it was time to make the switch.&lt;/p&gt;

&lt;p&gt;The only real "disadvantage" to using Vite is that it doesn't type-check your code, it only transpiles it to Javascript. I personally think this is fine as many IDE's nowadays have great Typescript support. &lt;/p&gt;

&lt;h2&gt;
  
  
  Migration steps
&lt;/h2&gt;

&lt;p&gt;Here are all of the steps I took to migrate from CRA to Vite. It's worth noting that I am migrating a Typescript project, though most of the steps should be similar to Javascript projects.&lt;/p&gt;

&lt;p&gt;Let's get started! 😀&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Install dependencies
&lt;/h3&gt;

&lt;p&gt;We need to install these 4 dependencies:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://vitejs.dev/"&gt;Vite&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/vitejs/vite/tree/main/packages/plugin-react"&gt;@vitejs/plugin-react&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/aleclarson/vite-tsconfig-paths"&gt;vite-tsconfig-paths&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/pd4d10/vite-plugin-svgr"&gt;vite-plugin-svgr&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--save-dev&lt;/span&gt; vite @vitejs/plugin-react vite-tsconfig-paths vite-plugin-svgr
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We need &lt;a href="https://github.com/aleclarson/vite-tsconfig-paths"&gt;vite-tsconfig-paths&lt;/a&gt; in order to tell Vite how to resolve absolute paths from the tsconfig file. This way you can import modules like this:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;import MyButton from 'components'&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;instead of&lt;/p&gt;

&lt;p&gt;&lt;code&gt;import MyButton from '../../../components'&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;We need &lt;a href="https://github.com/pd4d10/vite-plugin-svgr"&gt;vite-plugin-svgr&lt;/a&gt; in order to import SVGs as React components. For example:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;import { ReactComponent as Logo } from './logo.svg'&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  2. Create Vite config file
&lt;/h3&gt;

&lt;p&gt;Create a &lt;code&gt;vite.config.ts&lt;/code&gt; at the root of your project. This is where you specify all of the Vite configuration options.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;defineConfig&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;vite&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;react&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@vitejs/plugin-react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;viteTsconfigPaths&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;vite-tsconfig-paths&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;svgrPlugin&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;vite-plugin-svgr&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// https://vitejs.dev/config/&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;defineConfig&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="nx"&gt;react&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nx"&gt;viteTsconfigPaths&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nx"&gt;svgrPlugin&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;
  
  
  3. Move &lt;code&gt;index.html&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Move the &lt;code&gt;index.html&lt;/code&gt; file from the &lt;code&gt;/public&lt;/code&gt; folder out to the root of your project. Find out why this is done &lt;a href="https://vitejs.dev/guide/#index-html-and-project-root"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Update &lt;code&gt;index.html&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;URLs are treated a bit differently in Vite, so we'll have to remove all references of &lt;code&gt;%PUBLIC_URL%&lt;/code&gt;. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Before
&amp;lt;link rel="icon" href="%PUBLIC_URL%/favicon.ico" /&amp;gt;

// After
&amp;lt;link rel="icon" href="/favicon.ico" /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We need to also add an entry point to the &lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt; element:&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;noscript&amp;gt;&lt;/span&gt;You need to enable JavaScript to run this app.&lt;span class="nt"&gt;&amp;lt;/noscript&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"root"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="c"&gt;&amp;lt;!-- Add entry point 👇 --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"module"&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"/src/index.tsx"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  5. Update &lt;code&gt;tsconfig.json&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;The main things you need to update in the &lt;code&gt;tsconfig.json&lt;/code&gt; file are &lt;code&gt;target&lt;/code&gt;, &lt;code&gt;lib&lt;/code&gt; and &lt;code&gt;types&lt;/code&gt;. For example:&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;"compilerOptions"&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;"target"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ESNext"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"lib"&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;"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;"dom.iterable"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"esnext"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"types"&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;"vite/client"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"vite-plugin-svgr/client"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"allowJs"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"skipLibCheck"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"esModuleInterop"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"allowSyntheticDefaultImports"&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;"strict"&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;"forceConsistentCasingInFileNames"&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;"noFallthroughCasesInSwitch"&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;"module"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ESNext"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"moduleResolution"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"node"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"resolveJsonModule"&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;"isolatedModules"&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;"noEmit"&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;"jsx"&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"&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;"include"&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;"src"&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 can also take a look at Vite's &lt;code&gt;tsconfig.json&lt;/code&gt; file &lt;a href="https://github.com/vitejs/create-vite-app/blob/master/template-react-ts/tsconfig.json"&gt;here&lt;/a&gt; for reference.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Create &lt;code&gt;vite-env.d.ts&lt;/code&gt; file
&lt;/h3&gt;

&lt;p&gt;Since we're using Typescript, we need to create a &lt;code&gt;vite-env.d.ts&lt;/code&gt; file under the &lt;code&gt;src&lt;/code&gt; folder with the following contents:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;/// &amp;lt;reference types="vite/client" /&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  7. Remove &lt;code&gt;react-scripts&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;It's time to say goodbye to CRA once and for all. 👋 Run this command to uninstall it: &lt;code&gt;npm uninstall react-scripts&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We can also delete the &lt;code&gt;react-app-env.d.ts&lt;/code&gt; file. &lt;/p&gt;

&lt;h3&gt;
  
  
  8. Update scripts in &lt;code&gt;package.json&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Since we've removed &lt;code&gt;react-scripts&lt;/code&gt;, we now need to update the scripts within &lt;code&gt;package.json&lt;/code&gt; to reference &lt;code&gt;vite&lt;/code&gt; instead:&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;"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;"vite"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"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;"tsc &amp;amp;&amp;amp; vite build"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"serve"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"vite preview"&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;h3&gt;
  
  
  9. Start it up!
&lt;/h3&gt;

&lt;p&gt;Once you run &lt;code&gt;npm start&lt;/code&gt;, you should now hopefully see your app open in the browser, powered by the amazing Vite. &lt;/p&gt;

&lt;p&gt;If your app is small enough, this is all you might need to do. Otherwise, read on for more optional steps.&lt;/p&gt;

&lt;h2&gt;
  
  
  Optional steps
&lt;/h2&gt;

&lt;p&gt;Here are some additional optional steps you can take, depending on your own project setup.&lt;/p&gt;

&lt;h3&gt;
  
  
  ESLint &amp;amp; Prettier
&lt;/h3&gt;

&lt;p&gt;I've written a separate guide on how you can set up ESLint &amp;amp; Prettier &lt;a href="https://cathalmacdonnacha.com/setting-up-eslint-prettier-in-vitejs"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tests
&lt;/h3&gt;

&lt;p&gt;I've also written a guide on how you can replace Jest with Vitest &lt;a href="https://cathalmacdonnacha.com/migrating-from-jest-to-vitest"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Environmental variables
&lt;/h3&gt;

&lt;p&gt;It's pretty straightforward to migrate environmental variables, you simply rename &lt;code&gt;REACT_APP_&lt;/code&gt; to &lt;code&gt;VITE_&lt;/code&gt; within your &lt;code&gt;.env&lt;/code&gt; files. I just did a find and replace for these and it worked a treat.&lt;/p&gt;

&lt;p&gt;Then, instead of referencing the variables using &lt;code&gt;process.env.REACT_APP_&lt;/code&gt;, you'll use &lt;code&gt;import.meta.env.VITE_&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You can even go one step further and specify types for your environment variables by creating an &lt;code&gt;env.d.ts&lt;/code&gt; file in the &lt;code&gt;src&lt;/code&gt; folder. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;ImportMetaEnv&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;VITE_TOKEN&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;VITE_CLIENT_ID&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;ImportMeta&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;readonly&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;ImportMetaEnv&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you need to check for node environments (i.e development or production), you can do so by using the &lt;code&gt;import.meta.env&lt;/code&gt; object:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if (import.meta.env.DEV) {
  // do something in development mode only
}

if (import.meta.env.PROD) {
  // do something in production mode only
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For more on environmental variables, see &lt;a href="https://vitejs.dev/guide/env-and-mode.html"&gt;these Vite docs&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Change build output folder
&lt;/h3&gt;

&lt;p&gt;In Vite, the default production build folder name is &lt;code&gt;dist&lt;/code&gt;, you can change this to CRA's default &lt;code&gt;build&lt;/code&gt; folder if needed. I found this useful as my CI/CD scripts all referenced &lt;code&gt;build&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// vite.config.ts&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;defineConfig&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
  &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;outDir&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;build&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Automatically open the app on server start
&lt;/h3&gt;

&lt;p&gt;Something I liked about CRA is that it would automatically open the app in the browser on server start. Vite has this option too:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// vite.config.ts&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;defineConfig&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
  &lt;span class="na"&gt;server&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;open&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Change port number
&lt;/h3&gt;

&lt;p&gt;If you need to change the port number from the default &lt;code&gt;3000&lt;/code&gt;, specify like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// vite.config.ts&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;defineConfig&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
  &lt;span class="na"&gt;server&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;4000&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;h2&gt;
  
  
  Benchmarks
&lt;/h2&gt;

&lt;p&gt;Here about some benchmarks I recorded before and after the migration:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;CRA&lt;/th&gt;
&lt;th&gt;Vite&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;npm install&lt;/td&gt;
&lt;td&gt;21 seconds&lt;/td&gt;
&lt;td&gt;9 seconds&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Server startup time (cold)&lt;/td&gt;
&lt;td&gt;11 seconds&lt;/td&gt;
&lt;td&gt;856 milliseconds&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tests run&lt;/td&gt;
&lt;td&gt;17 seconds&lt;/td&gt;
&lt;td&gt;14 seconds&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Production build&lt;/td&gt;
&lt;td&gt;45 seconds&lt;/td&gt;
&lt;td&gt;17 seconds&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Production build size&lt;/td&gt;
&lt;td&gt;886 KB / gzip: 249 KB&lt;/td&gt;
&lt;td&gt;656.91 KB / gzip: 195.21 KB&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;You can really see the improvements in the server startup time, but other than that there wasn't a huge difference. Bear in mind though that this was a very small app, so could be even more important for those larger apps.&lt;/p&gt;

&lt;h2&gt;
  
  
  Troubleshooting
&lt;/h2&gt;

&lt;p&gt;Here are some errors you may come across:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. When running &lt;code&gt;npm start&lt;/code&gt;, I see the following error: &lt;code&gt;Error: Cannot find module 'node:path'&lt;/code&gt;.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Answer:&lt;/strong&gt; As per &lt;a href="https://github.com/vitejs/vite/issues/9113#issuecomment-1184319357"&gt;this issue&lt;/a&gt;, you have to make sure you have updated your &lt;code&gt;node&lt;/code&gt; version to &lt;code&gt;14.18.0&lt;/code&gt; or &lt;code&gt;v16+&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. When running &lt;code&gt;npm start&lt;/code&gt;, I see the following error: &lt;code&gt;No matching export in MODULE_NAME for import TYPE_NAME&lt;/code&gt;.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Answer:&lt;/strong&gt; This often happens when you're using a library with a &lt;code&gt;umd&lt;/code&gt; bundle, where Vite expects an &lt;code&gt;ESM&lt;/code&gt; bundle. This happened to me with &lt;a href="https://github.com/okta/okta-auth-js/issues/641"&gt;okta-auth-js&lt;/a&gt; and the fix was to specifcally tell Vite to load the &lt;code&gt;umd&lt;/code&gt; bundle in the Vite config file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// vite.config.ts&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;defineConfig&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
  &lt;span class="na"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;alias&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;@okta/okta-auth-js&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;@okta/okta-auth-js/dist/okta-auth-js.umd.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="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;strong&gt;3. My screen is blank after running &lt;code&gt;npm start&lt;/code&gt;.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Answer:&lt;/strong&gt; As per steps 3 and 4, make sure you've moved and updated the &lt;code&gt;index.html&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;Check out the &lt;a href="https://dev.toTroubleshooting"&gt;Vite troubleshooting docs&lt;/a&gt; for more.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final thoughts
&lt;/h2&gt;

&lt;p&gt;Overall I've been very happy with Vite. The migration was straightforward and the developer experience has improved significantly. It can do everything CRA can, but with better implementations. If you found this article helpful or have additional tips, please leave a comment below. 🙂&lt;/p&gt;

&lt;h2&gt;
  
  
  Want to see more?
&lt;/h2&gt;

&lt;p&gt;I mainly write about real tech topics I face in my everyday life as a Frontend Developer. If this appeals to you then feel free to follow me on Twitter: &lt;a href="https://twitter.com/cmacdonnacha"&gt;https://twitter.com/cmacdonnacha&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Bye for now 👋&lt;/p&gt;

</description>
      <category>vite</category>
      <category>cra</category>
      <category>react</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Setting up ESLint &amp; Prettier in ViteJS</title>
      <dc:creator>Cathal Mac Donnacha 🚀</dc:creator>
      <pubDate>Thu, 11 Aug 2022 09:52:50 +0000</pubDate>
      <link>https://forem.com/cathalmacdonnacha/setting-up-eslint-prettier-in-vitejs-5gig</link>
      <guid>https://forem.com/cathalmacdonnacha/setting-up-eslint-prettier-in-vitejs-5gig</guid>
      <description>&lt;p&gt;I recently migrated from create-react-app (CRA) to ViteJS, and as part of that, I set up ESLint and Prettier. In this article, I go through all the steps I took.&lt;/p&gt;

&lt;p&gt;Let's get started!&lt;/p&gt;

&lt;h1&gt;
  
  
  1. Install dependencies
&lt;/h1&gt;

&lt;p&gt;We need to install the following dependencies:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/eslint/eslint" rel="noopener noreferrer"&gt;ESLint&lt;/a&gt;: Our main linter.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/prettier/prettier" rel="noopener noreferrer"&gt;Prettier&lt;/a&gt;: Our main code formatter.&lt;/li&gt;
&lt;li&gt; &lt;a href="https://github.com/typescript-eslint/typescript-eslint/tree/main/packages/eslint-plugin" rel="noopener noreferrer"&gt;@typescript-eslint/eslint-plugin&lt;/a&gt;: An ESLint plugin which provides rules for TypeScript codebases.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/typescript-eslint/typescript-eslint/tree/main/packages/parser" rel="noopener noreferrer"&gt;@typescript-eslint/parser&lt;/a&gt;: A parser which allows ESLint to lint TypeScript source code.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/prettier/eslint-config-prettier" rel="noopener noreferrer"&gt;eslint-config-prettier&lt;/a&gt;: An ESLint configuration which disables the formatting rules in ESLint that Prettier is going to be responsible for handling, hence avoiding any clashes.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/import-js/eslint-plugin-import" rel="noopener noreferrer"&gt;eslint-plugin-import&lt;/a&gt;: Tells ESLint how to resolve imports.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/jsx-eslint/eslint-plugin-jsx-a11y" rel="noopener noreferrer"&gt;eslint-plugin-jsx-a11y&lt;/a&gt;: Checks for accessiblity issues.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/jsx-eslint/eslint-plugin-react" rel="noopener noreferrer"&gt;eslint-plugin-react&lt;/a&gt;: React specific linting rules for ESLint.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install eslint prettier @typescript-eslint/eslint-plugin @typescript-eslint/parser eslint-config-prettier eslint-plugin-import eslint-plugin-jsx-a11y eslint-plugin-react
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Something worth noting - when I looked into getting ESLint to work nicely with Prettier, I searched across many open source projects and found these 3 kept popping up:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/prettier/prettier-eslint" rel="noopener noreferrer"&gt;prettier-eslint&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/prettier/eslint-plugin-prettier" rel="noopener noreferrer"&gt;eslint-plugin-prettier&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/prettier/eslint-config-prettier" rel="noopener noreferrer"&gt;eslint-config-prettier&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I kept wondering which I should use as I saw that some projects used all, both, or only one. In the end, I came across &lt;a href="https://stackoverflow.com/a/44690309/3472935" rel="noopener noreferrer"&gt;this answer on Stack Overflow&lt;/a&gt; which helped me understand which plugin was most suitable (eslint-config-prettier).&lt;/p&gt;

&lt;h1&gt;
  
  
  2. Configure ESLint
&lt;/h1&gt;

&lt;p&gt;Now it's time to configure ESLint. &lt;/p&gt;

&lt;h3&gt;
  
  
  ESLint config file
&lt;/h3&gt;

&lt;p&gt;First let's create the &lt;code&gt;.eslintrc.js&lt;/code&gt; configuration file. I like to create mine as a javascript file so that I can add comments. Here's what it looks like:&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;extends&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="c1"&gt;// By extending from a plugin config, we can get recommended rules without having to add them manually.&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;eslint:recommended&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;plugin:react/recommended&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;plugin:import/recommended&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;plugin:jsx-a11y/recommended&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;plugin:@typescript-eslint/recommended&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="c1"&gt;// This disables the formatting rules in ESLint that Prettier is going to be responsible for handling.&lt;/span&gt;
    &lt;span class="c1"&gt;// Make sure it's always the last config, so it gets the chance to override other configs.&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;eslint-config-prettier&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;settings&lt;/span&gt;&lt;span class="p"&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="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// Tells eslint-plugin-react to automatically detect the version of React to use.&lt;/span&gt;
      &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;detect&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;// Tells eslint how to resolve imports&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;import/resolver&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;node&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;paths&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;src&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="na"&gt;extensions&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;.js&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;.jsx&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;.ts&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;.tsx&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="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="c1"&gt;// Add your own rules here to override ones from the extended configs.&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;
  
  
  ESLint ignore file
&lt;/h3&gt;

&lt;p&gt;Now we create an &lt;code&gt;.eslintignore&lt;/code&gt; file. This is where we tell ESLint which directories and files to ignore. This is project dependent, but here's an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;node_modules/
dist/
.prettierrc.js
.eslintrc.js
env.d.ts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Add a new script entry
&lt;/h3&gt;

&lt;p&gt;In your &lt;code&gt;package.json&lt;/code&gt; file, you can add a &lt;code&gt;lint&lt;/code&gt; script entry to run ESLint from the command line.&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;"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="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"lint"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"eslint . --ext .ts,.tsx"&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;Now run &lt;code&gt;npm run lint&lt;/code&gt; to check for linting errors. If you don't see any, it might mean that there are none (lucky you), so make sure to purposely add one in order to test it out e.g declaring a variable without using it.&lt;/p&gt;

&lt;h1&gt;
  
  
  3. Configure Prettier
&lt;/h1&gt;

&lt;p&gt;Now that we've taken care of ESLint, let's go ahead and create the &lt;code&gt;prettierrc.js&lt;/code&gt; file. This is where we specify all of our Prettier formatting rules. Here's an example:&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;trailingComma&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;all&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;tabWidth&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;semi&lt;/span&gt;&lt;span class="dl"&gt;"&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;singleQuote&lt;/span&gt;&lt;span class="dl"&gt;"&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;printWidth&lt;/span&gt;&lt;span class="dl"&gt;"&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;bracketSpacing&lt;/span&gt;&lt;span class="dl"&gt;"&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Prettier ignore file
&lt;/h3&gt;

&lt;p&gt;Similar to ESLint, we need to tell Prettier what files it should ignore. Again this will depend on your own project, but here's mine:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;node_modules/
dist/
.prettierrc.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  IDE integration
&lt;/h3&gt;

&lt;p&gt;In order to take full advantage of Prettier, you should be using it with an IDE to format your code after you save a file. If you're using VS Code, you can install the &lt;a href="https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode" rel="noopener noreferrer"&gt;Prettier extension&lt;/a&gt;. You can then set the following settings:&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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1659975153975%2FLlUi_TAGk.png%2520align%3D" 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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1659975153975%2FLlUi_TAGk.png%2520align%3D" alt="image.png"&gt;&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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1659975173416%2Fl_2Eohj_l.png%2520align%3D" 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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1659975173416%2Fl_2Eohj_l.png%2520align%3D" alt="image.png"&gt;&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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1659975223107%2FqZ1DjObGN.png%2520align%3D" 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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1659975223107%2FqZ1DjObGN.png%2520align%3D" alt="image.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Or if you have access to the &lt;code&gt;settings.json&lt;/code&gt; file, you can simply add these:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "prettier.configPath": ".prettierrc.js",
  "editor.defaultFormatter": "esbenp.prettier-vscode",
  "editor.formatOnSave": true
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, whenever you make changes to a file and save it, you should notice that Prettier auto formats it for you if needed. Pretty nice right? 😀&lt;/p&gt;

&lt;h1&gt;
  
  
  Automation
&lt;/h1&gt;

&lt;p&gt;If you'd like, you can go a bit further and automate the linting and formatting process a bit. I like to use &lt;a href="https://github.com/typicode/husky" rel="noopener noreferrer"&gt;Husky&lt;/a&gt; which allows you to run your linter, tests etc. on a git commit/push etc. You can also then use &lt;a href="https://github.com/azz/pretty-quick" rel="noopener noreferrer"&gt;pretty-quick&lt;/a&gt; along with &lt;code&gt;husky&lt;/code&gt; to automatically format your code whenever you &lt;code&gt;git commit&lt;/code&gt;, just in case someone on your team hasn't set it up in their IDE.&lt;/p&gt;

&lt;h2&gt;
  
  
  Want to see more?
&lt;/h2&gt;

&lt;p&gt;That's it for today! I mainly write about real tech topics I face in my everyday life as a Frontend Developer. If this appeals to you then feel free to follow me on Twitter: &lt;a href="https://twitter.com/cmacdonnacha" rel="noopener noreferrer"&gt;https://twitter.com/cmacdonnacha&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Bye for now 👋&lt;/p&gt;

</description>
      <category>vite</category>
      <category>react</category>
      <category>javascript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Migrating from Jest to Vitest</title>
      <dc:creator>Cathal Mac Donnacha 🚀</dc:creator>
      <pubDate>Wed, 25 May 2022 13:16:20 +0000</pubDate>
      <link>https://forem.com/cathalmacdonnacha/migrating-from-jest-to-vitest-45jo</link>
      <guid>https://forem.com/cathalmacdonnacha/migrating-from-jest-to-vitest-45jo</guid>
      <description>&lt;p&gt;I recently migrated from create-react-app (CRA) to ViteJS, and as part of that, I switched my test runner from Jest to &lt;a href="https://vitest.dev/"&gt;Vitest&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In this article, I go through all the steps I took as part of the migration, in the hope that it might help others who are going through the same process. &lt;/p&gt;

&lt;h1&gt;
  
  
  Why switch?
&lt;/h1&gt;

&lt;p&gt;I had originally planned to keep using Jest during the migration of CRA to ViteJS, but I kept running into issues, mainly because &lt;a href="https://jestjs.io/docs/ecmascript-modules"&gt;Jest support for ES Modules is still experimental&lt;/a&gt;. There is a Vite plugin called &lt;a href="https://github.com/sodatea/vite-jest"&gt;vite-jest&lt;/a&gt; but it's still very much a work in progress.&lt;/p&gt;

&lt;p&gt;Vitest is also pretty early in its development stages, but I felt that it was stable enough to give it a try, and I'm sure glad I did. It has many advantages but one thing I really like about it compared to other test runners is that it shares the same config file and plugins as Vite itself, so there's only one single pipeline to worry about.&lt;/p&gt;

&lt;h1&gt;
  
  
  Migration steps
&lt;/h1&gt;

&lt;p&gt;Here are all of the steps I took to migrate from Jest to Vitest. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note: These steps are unique to the way Jest was installed within CRA, so they may vary if you installed and configured Jest manually.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let's get started! &lt;/p&gt;

&lt;h2&gt;
  
  
  1. Install dependencies
&lt;/h2&gt;

&lt;p&gt;We need to install 3 dependencies:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://vitest.dev/"&gt;Vitest&lt;/a&gt;: Our test runner.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/jsdom/jsdom"&gt;jsdom&lt;/a&gt;: To mimic the browser while running tests.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/bcoe/c8"&gt;c8&lt;/a&gt;: To generate our code coverage reports.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To install these dev dependencies, run the following command:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;npm install --save-dev vitest jsdom c8&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Update vite.config.ts
&lt;/h2&gt;

&lt;p&gt;As I mentioned previously, the beauty of using Vitest is that it integrates seamlessly with Vite, so all we have to do is update the &lt;code&gt;vite.config.ts&lt;/code&gt; file. &lt;/p&gt;

&lt;p&gt;You'll also need to add a reference to the Vitest types using a &lt;a href="https://www.typescriptlang.org/docs/handbook/triple-slash-directives.html#-reference-types-"&gt;triple slash command&lt;/a&gt; at the top of your config file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/// &amp;lt;reference types="vitest" /&amp;gt;
import { defineConfig } from 'vite'

export default defineConfig({
  test: {
    // ...
  },
})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You may have different requirements, but here's what mine ended up looking like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/// &amp;lt;reference types="vitest" /&amp;gt;
import { defineConfig } from 'vite'

export default defineConfig({
  test: {
    globals: true,
    environment: 'jsdom',
    setupFiles: './src/setupTests.ts',
    coverage: {
      reporter: ['text', 'html'],
      exclude: [
        'node_modules/',
        'src/setupTests.ts',
      ],
    },
  },
});

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://vitest.dev/config/#deps"&gt;Here's the full list of config options&lt;/a&gt;, but I'll briefly explain each one I used:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;globals&lt;/code&gt;: Setting this to &lt;code&gt;true&lt;/code&gt; allows you to reference the APIs globally (describe, expect, it, should etc.), just like Jest.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;environment&lt;/code&gt;: This is where you choose what browser-like environment you want to use.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;setupFiles&lt;/code&gt;: This is the path to the setup files which run before each test file. In CRA, this file (setupFiles.ts) is included by default, so I left it as is.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;coverage&lt;/code&gt;: This is the configuration I use for the &lt;a href="https://github.com/bcoe/c8"&gt;c8&lt;/a&gt; reporter. I also specify the folders that I exclude from the report.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  3. Convert tests
&lt;/h2&gt;

&lt;p&gt;Vitest has been designed with a Jest compatible API (describe, expect, it, should etc.), so migrating tests was an absolute breeze. Unless you mock modules or methods, you probably won't need to do anything here.&lt;/p&gt;

&lt;p&gt;I was making use of Jest method mocking, but all I had to was change &lt;code&gt;jest.fn()&lt;/code&gt; to &lt;code&gt;vi.fn()&lt;/code&gt;. You'll have to import &lt;code&gt;vi&lt;/code&gt; in your test if you want to do the same: &lt;code&gt;import { vi } from 'vitest';&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Update package.json
&lt;/h2&gt;

&lt;p&gt;Let's update the &lt;code&gt;package.json&lt;/code&gt; scripts to reference &lt;code&gt;vitest&lt;/code&gt; instead of &lt;code&gt;react-scripts&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; "scripts": {
    ...
    "test": "vitest watch",
    "test:no-watch": "vitest run",
    "test:coverage": "vitest run --coverage"
  },
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll notice I also added a new entry to run code coverage and provided a way to run tests without a watcher, this comes in handy when running tests in a CI/CD pipeline.&lt;/p&gt;

&lt;p&gt;We can &lt;code&gt;npm uninstall @types/jest&lt;/code&gt; since we won't need it anymore. I have kept &lt;code&gt;@testing-library/jest-dom&lt;/code&gt; around though as it's required by React Testing Library.&lt;/p&gt;

&lt;p&gt;Finally, I removed any "jest" configuration I had in this file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  "jest": {
    "collectCoverageFrom": [
      "src/**/*.{js,ts,tsx}",
      "!/node_modules/",
    ]
  },
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  5. Update tsconfig.json
&lt;/h2&gt;

&lt;p&gt;To get TypeScript working with the global APIs, add &lt;code&gt;vitest/globals&lt;/code&gt; to the types field in your &lt;code&gt;tsconfig.json&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"types": ["vitest/globals", .....]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You may also run into an error like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;../node_modules/@types/jest/index.d.ts:34:1 - error TS6200: Definitions of the following identifiers conflict with those in another file: test, describe, it, expect, beforeAll, afterAll, beforeEach,
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At the time of writing this article, it still seems to be an &lt;a href="https://github.com/testing-library/jest-dom/issues/427"&gt;open issue&lt;/a&gt;. However, a workaround I found is to add &lt;code&gt;"skipLibCheck": true,&lt;/code&gt; to your &lt;code&gt;tsconfig.json&lt;/code&gt; file.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Run tests
&lt;/h2&gt;

&lt;p&gt;Hopefully the migration worked for you and all that's left to do now is to run &lt;code&gt;npm test&lt;/code&gt;. 🎉&lt;/p&gt;

&lt;h2&gt;
  
  
  Final thoughts
&lt;/h2&gt;

&lt;p&gt;Overall I'm extremely happy with Vitest, they've really made the migration so easy. I'm even more impressed with their docs given it's still pretty new, especially the number of &lt;a href="https://vitest.dev/guide/#examples"&gt;examples&lt;/a&gt; they have. If you found this article helpful or have suggestions, please leave a comment below. 🙂&lt;/p&gt;

&lt;h2&gt;
  
  
  Want to see more?
&lt;/h2&gt;

&lt;p&gt;I mainly write about real tech topics I face in my everyday life as a Frontend Developer. If this appeals to you then feel free to follow me on Twitter: &lt;a href="https://twitter.com/cmacdonnacha"&gt;https://twitter.com/cmacdonnacha&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Bye for now 👋&lt;/p&gt;

</description>
      <category>testing</category>
      <category>vite</category>
      <category>jest</category>
      <category>react</category>
    </item>
    <item>
      <title>Playwright E2E testing: Tips and best practices</title>
      <dc:creator>Cathal Mac Donnacha 🚀</dc:creator>
      <pubDate>Wed, 23 Mar 2022 13:07:28 +0000</pubDate>
      <link>https://forem.com/cathalmacdonnacha/playwright-e2e-testing-tips-and-best-practices-2g07</link>
      <guid>https://forem.com/cathalmacdonnacha/playwright-e2e-testing-tips-and-best-practices-2g07</guid>
      <description>&lt;p&gt;I've been using Playwright for a couple of months now, and though I'm certainly no expert, I've learned some tips, tricks and best practices along the way. In this article, we go through some of them, with the aim of helping you write even better E2E tests.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Prioritize user-facing attributes
&lt;/h2&gt;

&lt;p&gt;You should use user-facing attributes like text content, accessibility roles and label as much as possible. A user won't know what "id" or "class" means so why should we use them in our tests to find elements? Not only will your tests mimic how your users find elements, but they will also be more robust as user-facing attributes generally change less than ids, class names or other implementation details. &lt;/p&gt;

&lt;p&gt;For example, use &lt;code&gt;await page.locator('text=Login')&lt;/code&gt; instead of &lt;code&gt;await page.locator('#login-button')&lt;/code&gt;. A real user will find a button by its text content, not its id, so your tests should too.&lt;/p&gt;

&lt;p&gt;Remember, the more your tests resemble the way your software is used, the more confidence they can give you.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Use locators over selectors
&lt;/h2&gt;

&lt;p&gt;Using &lt;a href="https://playwright.dev/docs/locators"&gt;locators&lt;/a&gt; will help prevent flakiness or unnoticed breakages when your web page changes. These breakages have the potential to go unnoticed when using standard selectors. &lt;/p&gt;

&lt;p&gt;For example, use &lt;code&gt;await page.locator('text=Login').click()&lt;/code&gt; instead of &lt;code&gt;await page.click('text=Login')&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The main reason locators help mitigate flakiness is down to its level of strictness. There are three possible outcomes when using locators:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Test works as expected.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Selector does not match anything and test breaks loudly.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Multiple elements match the selector (e.g. there is a second "Login" button added to the page somewhere), and the locator complains about this and the test breaks with a nice error message.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This means you don't have to be very thoughtful of selectors, and picking just &lt;code&gt;text=Login&lt;/code&gt; is totally fine - Playwright will do all the heavy lifting to ensure correct and non-flaky testing.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Use Page Object Model (POM)
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://martinfowler.com/bliki/PageObject.html"&gt;Page Object Model&lt;/a&gt; is a common pattern that can help avoid duplication, improve maintainability and simplify interactions between pages in multiple tests. &lt;/p&gt;

&lt;p&gt;Writing tests using POM feels more natural as it conveys more intent and encourages behaviour over raw mechanics. Playwright have included &lt;a href="https://playwright.dev/docs/test-pom"&gt;this example&lt;/a&gt; in their docs to give you an idea on how to implement it.&lt;/p&gt;

&lt;p&gt;In saying that, you don't always have to use POM either. Use it when it makes sense to abstract. I often start without POM and only create page object models when I feel that the tests will benefit from it.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Duplication is far cheaper than the wrong abstraction - Sandi Metz.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  4. Use double quotes to find specific elements
&lt;/h2&gt;

&lt;p&gt;If you are finding multiple elements with the same partial string, try using double quotes to enable case sensitivity. For example, &lt;code&gt;await page.locator('text=Checkout')&lt;/code&gt; could return two elements as it finds a "Checkout" button and a "Check out this new shoe" heading. Use double quotes if you only want to return the button on its own e.g &lt;code&gt;await page.locator('text="Checkout"')&lt;/code&gt;. See &lt;a href="https://playwright.dev/docs/selectors#text-selector"&gt;Playwright text selectors&lt;/a&gt; for more.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Avoid selectors tied to implementation
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;xpath&lt;/code&gt; and &lt;code&gt;css&lt;/code&gt; can be tied to the DOM structure or implementation. For example:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;await page.locator('#tsf &amp;gt; div:nth-child(2) &amp;gt; div.A8SBwf &amp;gt; div.RNNXgb &amp;gt; div &amp;gt; div.a4bIc &amp;gt; input').click();&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Pretty gnarly right? These selectors can break when the DOM structure changes, so it's best to avoid relying on them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final thoughts
&lt;/h2&gt;

&lt;p&gt;That's all the tips I have for you today. If you have any tips or best practices of your own, please share them in the comments below. 🙂&lt;/p&gt;

&lt;h2&gt;
  
  
  Want to see more?
&lt;/h2&gt;

&lt;p&gt;I mainly write about real tech topics I face in my everyday life as a Frontend Developer. If this appeals to you then feel free to follow me on Twitter: &lt;a href="https://twitter.com/cmacdonnacha"&gt;https://twitter.com/cmacdonnacha&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Bye for now 👋&lt;/p&gt;

</description>
      <category>testing</category>
      <category>playwright</category>
      <category>javascript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Working around CORS in create-react-app</title>
      <dc:creator>Cathal Mac Donnacha 🚀</dc:creator>
      <pubDate>Tue, 01 Feb 2022 14:48:47 +0000</pubDate>
      <link>https://forem.com/cathalmacdonnacha/working-around-cors-in-create-react-app-3pf2</link>
      <guid>https://forem.com/cathalmacdonnacha/working-around-cors-in-create-react-app-3pf2</guid>
      <description>&lt;p&gt;One problem we often face as frontend developers is dealing with CORS when making API requests.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is CORS?
&lt;/h2&gt;

&lt;p&gt;CORS (Cross-Origin Resource Sharing) is essentially a mechanism that allows a server to express what other domains can make requests to it. For example, my app could be hosted on &lt;code&gt;http://domain-a.com&lt;/code&gt;, so only requests made from that same domain are allowed. If another app hosted on &lt;code&gt;http://domain-b.com&lt;/code&gt; tried to make a request to &lt;code&gt;http://domain-a.com&lt;/code&gt;, it would fail depending on the CORS policy.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where does CRA fit in?
&lt;/h2&gt;

&lt;p&gt;While using CRA (create-react-app), I've often run into a situation where I want to test my local changes against another team's API endpoint. I mean, there's only so much mocking you can rely on! By default, CRA runs locally on &lt;code&gt;http://localhost:3000&lt;/code&gt;, so if I try to make an API request out to &lt;code&gt;http://domain-a.com/users.json&lt;/code&gt;, CORS would block it. However, when developing locally, CRA lets us get around this by proxying all unknown requests to a specified domain. This can now help us make sure our frontend code lines up with the backend's responses. &lt;/p&gt;

&lt;h2&gt;
  
  
  What's the workaround?
&lt;/h2&gt;

&lt;p&gt;All we need to do is add one new field in &lt;code&gt;package.json&lt;/code&gt;. For example:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;"proxy": "http://domain-a.com",&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;After you restart your CRA dev server, you should now be free to make requests to &lt;code&gt;http://domain-a.com/users.json&lt;/code&gt;. This will of course only work locally and you should only make requests to a dev API endpoint, not production. That's it!&lt;/p&gt;

&lt;h2&gt;
  
  
  Want to see more?
&lt;/h2&gt;

&lt;p&gt;I mainly write about real tech topics I face in my everyday life as a Frontend Developer. If this appeals to you then feel free to follow me on Twitter: &lt;a href="https://twitter.com/cmacdonnacha"&gt;https://twitter.com/cmacdonnacha&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Bye for now 👋&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>frontend</category>
      <category>cors</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Cypress vs Playwright: Which is best for E2E testing?</title>
      <dc:creator>Cathal Mac Donnacha 🚀</dc:creator>
      <pubDate>Thu, 16 Dec 2021 13:56:14 +0000</pubDate>
      <link>https://forem.com/cathalmacdonnacha/cypress-vs-playwright-which-is-best-for-e2e-testing-5f52</link>
      <guid>https://forem.com/cathalmacdonnacha/cypress-vs-playwright-which-is-best-for-e2e-testing-5f52</guid>
      <description>&lt;p&gt;Cypress was our go-to end-to-end (E2E) testing tool, and we were pretty happy with it, up until recently that is. Lately, we've run into a couple of testing scenarios where Cypress support has been limited, notably around multiple domains/tabs and iFrames. This caused us to re-evaluate the available E2E tools and it ultimately came down to two options; keep Cypress or switch to Playwright. &lt;/p&gt;

&lt;p&gt;In this article, I compare both tools in the format of an &lt;a href="https://adr.github.io/madr/#overview"&gt;ADR&lt;/a&gt;, which should help us decide which tool to go with. Let the battle commence!&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Cypress&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.cypress.io/"&gt;https://www.cypress.io/&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Good, because it's very easy to learn and get set up with basic tests.&lt;/li&gt;
&lt;li&gt;Good, because it has a nice dashboard to view test reports, analytics and recordings.&lt;/li&gt;
&lt;li&gt;Good, because it supports Chromium and Firefox.&lt;/li&gt;
&lt;li&gt;Good, because it has a very slick Test Runner UI.&lt;/li&gt;
&lt;li&gt;Good, because it's built specifically for end-to-end testing.&lt;/li&gt;
&lt;li&gt;Good, because you can edit your test code in the browser and instantly see it run as you change the code.&lt;/li&gt;
&lt;li&gt;Good, because it's mature and has good community support.&lt;/li&gt;
&lt;li&gt;Bad, because it doesn't support multiple domains.&lt;/li&gt;
&lt;li&gt;Bad, because authentication requires a lot more setup due to lack of multi-domain support.&lt;/li&gt;
&lt;li&gt;Bad, because it doesn't support Webkit (Safari)&lt;/li&gt;
&lt;li&gt;Bad, because you cannot run tests against multiple browsers at the same time.&lt;/li&gt;
&lt;li&gt;Bad, because iFrame support is limited.&lt;/li&gt;
&lt;li&gt;Bad, because there is no "hover" support.&lt;/li&gt;
&lt;li&gt;Bad, because the chaining command syntax can quickly get out of hand for more complex tests.&lt;/li&gt;
&lt;li&gt;Bad, because you have to pay a premium to get access to some dashboard features (e.g flake detection)&lt;/li&gt;
&lt;li&gt;Bad, because to do parallelization well, it requires vendor-locked software.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Playwright&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://playwright.dev/"&gt;https://playwright.dev/&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Good, because it supports Chromium, Firefox and Webkit (Safari).&lt;/li&gt;
&lt;li&gt;Good, because it supports multiple domains and tabs.&lt;/li&gt;
&lt;li&gt;Good, because it supports 5 language bindings (Javascript, Typescript, Java, Python, .NET)&lt;/li&gt;
&lt;li&gt;Good, because it's &lt;a href="https://blog.checklyhq.com/cypress-vs-selenium-vs-playwright-vs-puppeteer-speed-comparison/"&gt;fast&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Good, because you can run tests against multiple browsers at the same time.&lt;/li&gt;
&lt;li&gt;Good, because it fully supports parallelization, even locally.&lt;/li&gt;
&lt;li&gt;Good, because it supports parallel tests within a single test file.&lt;/li&gt;
&lt;li&gt;Good, because it's Javascript first, so feels more natural.&lt;/li&gt;
&lt;li&gt;Good, because it has "hover" support.&lt;/li&gt;
&lt;li&gt;Good, because iFrames are natively supported.&lt;/li&gt;
&lt;li&gt;Good, because it supports reuse of authentication state to speed up tests.&lt;/li&gt;
&lt;li&gt;Good, because it lets you choose your test runner (e.g. Jest but the default one is advised)&lt;/li&gt;
&lt;li&gt;Good, because signing in is simple, you just fill in the form.&lt;/li&gt;
&lt;li&gt;Good, because it's completely free.&lt;/li&gt;
&lt;li&gt;Good, because it has few dependencies.&lt;/li&gt;
&lt;li&gt;Bad, because it's still quite new, so possibility of a smaller community.&lt;/li&gt;
&lt;li&gt;Bad, because 3rd party tutorials are out of date due to changing API.&lt;/li&gt;
&lt;li&gt;Bad, because it's a mix between an automation and testing framework.&lt;/li&gt;
&lt;li&gt;Bad, because it has a steeper learning curve.&lt;/li&gt;
&lt;li&gt;Bad, because it does not have a dedicated dashboard so would be harder to debug tests remotely.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Common features between both&lt;/strong&gt;
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Good documentation&lt;/li&gt;
&lt;li&gt;API testing&lt;/li&gt;
&lt;li&gt;Point &amp;amp; click test recording&lt;/li&gt;
&lt;li&gt;Test debugging tools&lt;/li&gt;
&lt;li&gt;Test retries&lt;/li&gt;
&lt;li&gt;Automatic waiting&lt;/li&gt;
&lt;li&gt;Video and screen capture&lt;/li&gt;
&lt;li&gt;Mobile emulation&lt;/li&gt;
&lt;li&gt;Regularly updated and well maintained&lt;/li&gt;
&lt;li&gt;Run only a subset of tests&lt;/li&gt;
&lt;li&gt;Network monitoring&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;In the end, we decided to go with Playwright, mainly because of its native support for multiple domains, tabs and iFrames. I will say that I found Cypress' debugging to be more developer-friendly and in general "slicker", but that wasn't enough to make us stay put. &lt;/p&gt;

&lt;h2&gt;
  
  
  Want to see more?
&lt;/h2&gt;

&lt;p&gt;I mainly write about real tech topics I face in my everyday life as a Frontend Developer. If this appeals to you then feel free to follow me on Twitter: &lt;a href="https://twitter.com/cmacdonnacha"&gt;https://twitter.com/cmacdonnacha&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Bye for now 👋&lt;/p&gt;

</description>
      <category>testing</category>
      <category>javascript</category>
      <category>webdev</category>
      <category>react</category>
    </item>
    <item>
      <title>Why you should make your tests fail</title>
      <dc:creator>Cathal Mac Donnacha 🚀</dc:creator>
      <pubDate>Thu, 02 Dec 2021 13:58:36 +0000</pubDate>
      <link>https://forem.com/cathalmacdonnacha/why-you-should-make-your-tests-fail-2oof</link>
      <guid>https://forem.com/cathalmacdonnacha/why-you-should-make-your-tests-fail-2oof</guid>
      <description>&lt;p&gt;Let's face it, most of us developers don't necessarily love writing tests. We sometimes end up rushing through them, and once we see that green tick next to a passing test, we're generally pretty happy to move on. However, an enemy is lurking amongst us.&lt;/p&gt;

&lt;h2&gt;
  
  
  False positive test
&lt;/h2&gt;

&lt;p&gt;The enemy I'm talking about here is otherwise known as a false positive test. Let's take a look at what this beast looks like.&lt;/p&gt;

&lt;p&gt;Here we have a &lt;code&gt;select&lt;/code&gt; element with some countries as options:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;select&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;option&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Select a country&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;option&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;option&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"US"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;United States&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;option&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;option&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"IE"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Ireland&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;option&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;option&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"AT"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Austria&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;option&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;select&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's my test:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should allow user to change country&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="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;App&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;userEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;selectOptions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getByRole&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;combobox&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getByRole&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;option&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;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Ireland&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="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getByRole&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;option&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;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Ireland&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})).&lt;/span&gt;&lt;span class="nx"&gt;toBeInTheDocument&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The test passes, isn't that great? ✅  I'm afraid not. 😭  Let's see why after we intentionally make it fail.&lt;/p&gt;

&lt;h2&gt;
  
  
  Making your test fail
&lt;/h2&gt;

&lt;p&gt;Here's a real example of a false positive test situation I ran into recently:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should allow user to change country&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="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;App&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;userEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;selectOptions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getByRole&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;combobox&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getByRole&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;option&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;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Ireland&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="c1"&gt;// Changed expected country from "Ireland" to "Austria" - this should fail.&lt;/span&gt;
  &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getByRole&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;option&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;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Austria&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})).&lt;/span&gt;&lt;span class="nx"&gt;toBeInTheDocument&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;I was expecting the check for "Austria" to fail because it wasn't the selected country, and I was pretty surprised to see that it was still passing. Looks like we have just identified a false positive test.&lt;/p&gt;

&lt;p&gt;Let us take a step back. The purpose of my test is to ensure that when changing a country, it is indeed the now selected option. However, after debugging for while I eventually realised that the test above only checks that the country "Ireland" exists, instead of checking if it's selected.&lt;/p&gt;

&lt;p&gt;Here's how I eventually fixed it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should allow user to change country&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="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;App&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;userEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;selectOptions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getByRole&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;combobox&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getByRole&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;option&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;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Ireland&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="c1"&gt;// Now checking if the option is selected&lt;/span&gt;
  &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getByRole&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;option&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;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Ireland&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nx"&gt;selected&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toBe&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, I am correctly checking that the option is selected and all is good. I wouldn't have found this unless I intentionally made my test fail, so I'm glad my persistence has paid off and I avoided a potential bug. &lt;/p&gt;

&lt;h2&gt;
  
  
  Final thoughts
&lt;/h2&gt;

&lt;p&gt;I've been burnt enough times in the past by false positive tests that I have vouched to always intentionally make my tests fail before moving on to the next one. Since doing this, I've gotten a lot more confident in my tests knowing that they'll only pass in the correct circumstances. &lt;/p&gt;

&lt;p&gt;That's about all I have to share with you today. Let me know in the comments if you found this article useful. 🙌 &lt;/p&gt;

&lt;h2&gt;
  
  
  Want to follow along?
&lt;/h2&gt;

&lt;p&gt;I mainly write about real tech topics I face in my everyday life as a Frontend Developer. If this appeals to you then feel free to follow me on Twitter: &lt;a href="https://twitter.com/cmacdonnacha"&gt;https://twitter.com/cmacdonnacha&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Bye for now 👋&lt;/p&gt;

</description>
      <category>testing</category>
      <category>javascript</category>
      <category>webdev</category>
      <category>beginners</category>
    </item>
    <item>
      <title>6 tips every code review author should know</title>
      <dc:creator>Cathal Mac Donnacha 🚀</dc:creator>
      <pubDate>Mon, 08 Nov 2021 13:59:50 +0000</pubDate>
      <link>https://forem.com/cathalmacdonnacha/6-tips-to-improve-your-code-reviews-4kac</link>
      <guid>https://forem.com/cathalmacdonnacha/6-tips-to-improve-your-code-reviews-4kac</guid>
      <description>&lt;p&gt;I've submitted my fair share of code reviews at this point of my frontend development career and I've picked up a couple of tips along the way, so I wanted to share some of them with you.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Keep it small 🤏
&lt;/h2&gt;

&lt;p&gt;Try and keep your code reviews as small as possible. This is better for reviewers as it makes it easier for them to focus on one or two goals. It's also better for you as it means you can push your code to production more often, giving yourself a sense of achievement. &lt;/p&gt;

&lt;p&gt;As a code reviewer, there's nothing worse than getting a huge code review that takes days to go through. You'll find a reviewer's attention to detail will drop off after 60 minutes of review time, meaning you won't get the best out of your reviews.&lt;/p&gt;

&lt;p&gt;By keeping your reviews small, you're more likely to get faster feedback and approvals, meaning you can ship your code more often. Win. 🚀&lt;/p&gt;

&lt;p&gt;In saying this, there are times where your code reviews simply can't be small. This may occur if you're making changes that affect multiple services or you're working with legacy code that can't be broken down as easily. This is fine too, these tips are just a guide to follow when you can.&lt;/p&gt;

&lt;p&gt;As a frontend developer, I try to break down large stories into multiple smaller tasks, meaning I can submit smaller code reviews too. I often follow Martin Fowler's &lt;a href="https://martinfowler.com/bliki/KeystoneInterface.html"&gt;Keystone Interface&lt;/a&gt; method to achieve this.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Write a good description 👨‍💻
&lt;/h2&gt;

&lt;p&gt;Include a brief description in your code review where you give a summary of the changes made. This is very important as it gives the reviewer the context required to put themselves in the user's shoes while they review your code.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Include screenshots or recordings 📹
&lt;/h2&gt;

&lt;p&gt;If the changes you have made are visual then it's important to include screenshots, or even better...screen recordings. Similar to the previous tip, this gives the reviewer some more context into the changes you've made as they can link your code with what they see in your screenshots or recordings. As they say, &lt;em&gt;"a picture paints a thousand words"&lt;/em&gt;. I'm not sure if your screenshot will end up in any art galleries, but you can be sure they will benefit your reviewers. 😀&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Give instructions on how to verify your changes 👨‍🏫
&lt;/h2&gt;

&lt;p&gt;Depending on the code review, it's important to give your reviewers a way to play around with your changes for themselves. This is the number one best way to receive feedback, especially if the changes are visual. I've often found small issues during code reviews relating to how responsive a webpage is, which is something I wouldn't have spotted by looking at the code or screenshots.&lt;/p&gt;

&lt;p&gt;On our team, we often use &lt;a href="https://devcenter.heroku.com/articles/github-integration-review-apps"&gt;Heroku review apps&lt;/a&gt; which gives you a sandboxed version of your app. It gets deployed whenever we create a new code review. This has become a fundamental part of our code review process. However, most teams won't have this luxury so another good option is to ask the reviewer to checkout your branch and run it locally.&lt;/p&gt;

&lt;p&gt;Here's an example of some instructions you could include in your code review:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Checkout branch: &lt;code&gt;popup-toast-notification&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Run app locally: &lt;code&gt;npm start&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Go to the "Profile" page.&lt;/li&gt;
&lt;li&gt;Change your name.&lt;/li&gt;
&lt;li&gt;Click "Update".&lt;/li&gt;
&lt;li&gt;You should see a toast notification appear at the top of your screen.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  5. Give reviewers enough time ⏱️
&lt;/h2&gt;

&lt;p&gt;There is no point in requesting a large code review on the very last day of the sprint. It's important to give reviewers enough time so that they don't feel rushed or pressured into giving approvals. By giving reviewers the time they need, they can give you adequate feedback and it also gives you that extra bandwidth to make any requested changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Avoid scope creep ✋
&lt;/h2&gt;

&lt;p&gt;As engineers, we always strive to develop the perfect solution. This can be good, but it can also lead to distractions in code reviews where reviewers will request extra features. How many times have you heard someone say &lt;em&gt;"This is good, could you add this X feature to make it even better?"&lt;/em&gt;. As the code review author, it's important to know when a request is outside the scope of the task you're working on. Getting feature request ideas is fantastic, but it's something you should discuss with your product manager and team members, and possibly create a new task for it. This could then be delivered as part of a separate code review.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final thoughts 🤔
&lt;/h2&gt;

&lt;p&gt;That's it. 😀 Hopefully this article can help you improve your code reviews, but remember that these are just tips to guide you and so it's important to figure out what works for you and your team.&lt;/p&gt;

&lt;p&gt;If you found this article useful then please give it a like, and if you have some tips of your own, feel free to leave a comment.  🙌&lt;/p&gt;

&lt;h2&gt;
  
  
  Want more? 📢
&lt;/h2&gt;

&lt;p&gt;I mainly write about real-world tech topics I face in my everyday life as a frontend developer. If this appeals to you, then feel free to follow me on Twitter for more: &lt;a href="https://twitter.com/cmacdonnacha"&gt;https://twitter.com/cmacdonnacha&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Bye for now 👋&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>codereview</category>
      <category>webdev</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Tips for developers switching from Windows to Mac</title>
      <dc:creator>Cathal Mac Donnacha 🚀</dc:creator>
      <pubDate>Mon, 01 Nov 2021 13:07:27 +0000</pubDate>
      <link>https://forem.com/cathalmacdonnacha/tips-for-developers-switching-from-windows-to-mac-3d51</link>
      <guid>https://forem.com/cathalmacdonnacha/tips-for-developers-switching-from-windows-to-mac-3d51</guid>
      <description>&lt;p&gt;I was a Windows fan all my life, both at home since I was 10 years old, and at work for 8 years of my frontend development career. However, when I moved jobs recently I was given a MacBook Pro, and so I had little choice but to dive headfirst into everything MacOS. It was a bit strange at first and it honestly took me a few days to get settled in with the Mac, but once I did, I really started to like it.&lt;/p&gt;

&lt;p&gt;From the beginning, there were a couple of things I missed about Windows, and some alternative apps that I had to find. In this article, I go through some of the things that helped me, in the hope that I can help smoothen the ride for other developers starting out on their Windows to Mac journey.&lt;/p&gt;

&lt;h2&gt;
  
  
  Package manager 📦
&lt;/h2&gt;

&lt;p&gt;I used to use &lt;a href="https://chocolatey.org/" rel="noopener noreferrer"&gt;Chocolatey&lt;/a&gt; as my package manager on Windows. I found it really useful to install and organise all of my various packages, and so I wanted to find the best alternative for Mac. &lt;a href="https://brew.sh/" rel="noopener noreferrer"&gt;Brew&lt;/a&gt; was the answer! I've been using it for the past year and haven't had any issues or missing features compared to Chocolatey.&lt;/p&gt;

&lt;h2&gt;
  
  
  Terminal 📺
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://conemu.github.io/" rel="noopener noreferrer"&gt;ConEmu&lt;/a&gt; was my go-to terminal on Windows and so I was pretty bummed out when I realised that it wasn't supported on Mac. After asking some folks on my team, they suggested &lt;a href="https://iterm2.com/" rel="noopener noreferrer"&gt;iTerm2&lt;/a&gt; and I must admit it's equally fantastic.&lt;/p&gt;

&lt;p&gt;One feature that is not included in iTerm2 though is the ability to display the currently active git branch at a glance. Worry not! &lt;a href="https://ohmyz.sh/" rel="noopener noreferrer"&gt;oh-my-zsh&lt;/a&gt; to the rescue 🦸‍♂️  You can install it using &lt;a href="https://ohmyz.sh/#install" rel="noopener noreferrer"&gt;this curl command&lt;/a&gt; and hey presto, you now see those nice pretty git branches in your terminal.&lt;/p&gt;

&lt;p&gt;Similar to ConEmu, I use a feature in iTerm2 called "window arrangements" to automatically launch tabs whenever I open the terminal. I find this really handy, as it automatically navigates to the correct directory and opens my regular 3 &lt;code&gt;git&lt;/code&gt;, &lt;code&gt;serve&lt;/code&gt; and &lt;code&gt;test&lt;/code&gt; tabs for me. This feature has saved me countless hours over the years!&lt;/p&gt;

&lt;h2&gt;
  
  
  Finder 🗄
&lt;/h2&gt;

&lt;p&gt;Finder is the default file manager on the Mac and it's equivalent to Windows Explorer, with some differences. The biggest difference for me was the lack of a classic cut and paste. On Windows you could simply press &lt;code&gt;ctrl&lt;/code&gt; + &lt;code&gt;x&lt;/code&gt; and &lt;code&gt;ctrl&lt;/code&gt; + &lt;code&gt;v&lt;/code&gt;. However, to do this on Mac you have to select the file and then press &lt;code&gt;cmd&lt;/code&gt; + &lt;code&gt;c&lt;/code&gt; and &lt;code&gt;cmd&lt;/code&gt; + &lt;code&gt;option&lt;/code&gt; + &lt;code&gt;v&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Hidden Files
&lt;/h3&gt;

&lt;p&gt;To show hidden files in Finder just press &lt;code&gt;command&lt;/code&gt; + &lt;code&gt;shift&lt;/code&gt; + &lt;code&gt;.&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;If you want to show hidden files by default just open your terminal and run this command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;defaults write com.apple.finder AppleShowAllFiles YES&lt;span class="p"&gt;;&lt;/span&gt; killall Finder&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Split screen 💻
&lt;/h2&gt;

&lt;p&gt;I often use split screen to view both the browser and VS Code side by side. On Windows, this is quite simple to do using a feature called &lt;a href="https://support.microsoft.com/en-us/windows/snap-your-windows-885a9b1e-a983-a3b1-16cd-c531795e6241#WindowsVersion=Windows_10" rel="noopener noreferrer"&gt;Snap Assist&lt;/a&gt;. You simply just drag a window to the edge of the screen and then select the other window you want to show beside it.&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%2Fi.imgur.com%2FS4tZrd0.gif" 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%2Fi.imgur.com%2FS4tZrd0.gif" alt="Windows Split View"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Again I was pretty disappointed to find out that you couldn't easily achieve this on the Mac. Not to worry though, it does have a feature called &lt;a href="https://support.apple.com/en-ie/HT204948" rel="noopener noreferrer"&gt;Split View&lt;/a&gt;, which is close enough for me. It's not as quick and easy but gets the job done.&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%2Fi.imgur.com%2FP0H2Vd9.gif" 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%2Fi.imgur.com%2FP0H2Vd9.gif" alt="Mac Split View"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Window management app
&lt;/h3&gt;

&lt;p&gt;A reader suggested &lt;a href="https://rectangleapp.com/" rel="noopener noreferrer"&gt;Rectangle&lt;/a&gt; for split screen window management and I must say, I'm impressed!&lt;/p&gt;

&lt;h2&gt;
  
  
  MS Paint 🎨
&lt;/h2&gt;

&lt;p&gt;Oh man, how I loved this app. As a frontend developer, I was constantly taking screenshots, pasting them into Paint and then drawing arrows, boxes and text on top of them to include in JIRA tickets, emails etc. I even remember using it to create some icons back in the day. I couldn't believe the Mac didn't have a native paint app. However, I found two solutions that helped me dry my tears.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Shape detection in Preview
&lt;/h3&gt;

&lt;p&gt;I may be easily amused, but this feature blew my mind when I first tried it. When you take a screenshot (&lt;code&gt;command&lt;/code&gt; + &lt;code&gt;shift&lt;/code&gt; + &lt;code&gt;3&lt;/code&gt;), a thumbnail will appear in the bottom right corner of the screen. Once you click on the thumbnail, the native Preview app on the Mac will open. From here you can draw a rough circle, square, or arrow and if it's recognised as a standard shape, it’s replaced by that shape. How cool is that?!&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%2Fi.imgur.com%2FfZPwazm.gif" 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%2Fi.imgur.com%2FfZPwazm.gif" alt="Shape detection in Preview app"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Sketchpad
&lt;/h3&gt;

&lt;p&gt;One drawback to the native Preview app is that you can't simply copy and paste two images onto one canvas. Again, I do this a lot when creating "before and after" screenshots of tasks I'm working on. It's so much easier to convey this in one single side-by-side image instead of two separate ones.&lt;/p&gt;

&lt;p&gt;Again MS Paint was great for this, so I had to find an alternative. I tried lots of Mac apps, browser extensions and web apps but struggled to find anything suitable, user friendly, and free, which had similar features to MS Paint. Finally, I found it! &lt;a href="https://sketch.io/sketchpad/" rel="noopener noreferrer"&gt;Sketchpad&lt;/a&gt; is a fantastic web app where you can quickly copy and paste images side by side, draw, add text, fill vectors and lots more.&lt;/p&gt;

&lt;h2&gt;
  
  
  Screenshots and video recording 📹
&lt;/h2&gt;

&lt;p&gt;I touched on this earlier, but as a frontend developer, I take &lt;strong&gt;a lot&lt;/strong&gt; of screenshots and recordings so that I can attach them to JIRA tickets, Slack messages, emails and so on. I think that this is an important part of any frontend developer's workflow, so I wanted to go through it in a bit more detail.&lt;/p&gt;

&lt;h3&gt;
  
  
  Basic screenshot
&lt;/h3&gt;

&lt;p&gt;Using the &lt;code&gt;command&lt;/code&gt; + &lt;code&gt;shift&lt;/code&gt; + &lt;code&gt;3&lt;/code&gt; shortcut, you can take a screenshot of the currently active screen. You should then see a thumbnail appear in the right bottom corner of your screen. Clicking on this will bring up the native Preview app, which (as mentioned earlier) you can use to draw on the image. It will save the image to your default screenshots folder on your Mac, which is useful if you want to refer back to it later.&lt;/p&gt;

&lt;h3&gt;
  
  
  Capture a portion of the screen
&lt;/h3&gt;

&lt;p&gt;Using the &lt;code&gt;command&lt;/code&gt; + &lt;code&gt;shift&lt;/code&gt; + &lt;code&gt;4&lt;/code&gt; shortcut, you can take a screenshot of a portion of the screen. This will save the image to your Mac. Out of all the screenshot options, I probably use this the most. &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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1635622962173%2FsbZw41Co2.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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1635622962173%2FsbZw41Co2.png" alt="Capture a portion of the screen"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Capture a portion of the screen and copy to clipboard
&lt;/h3&gt;

&lt;p&gt;Using the &lt;code&gt;command&lt;/code&gt; + &lt;code&gt;shift&lt;/code&gt; + &lt;code&gt;control&lt;/code&gt; +  &lt;code&gt;4&lt;/code&gt; shortcut, you can take a screenshot of a portion of the screen. The difference between this and the previous shortcut is that it will only copy the image to your clipboard. It will not save the image to your Mac. This is useful if you want to quickly copy and paste screenshots into chat or an email body, but not take up space on your hard drive.&lt;/p&gt;

&lt;h3&gt;
  
  
  Screenshot a window
&lt;/h3&gt;

&lt;p&gt;Using the &lt;code&gt;command&lt;/code&gt; + &lt;code&gt;shift&lt;/code&gt; + &lt;code&gt;control&lt;/code&gt; +  &lt;code&gt;space&lt;/code&gt; shortcut, you can take a screenshot of a particular window.&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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1635622880504%2FcVoCEuyxf.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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1635622880504%2FcVoCEuyxf.png" alt="Screenshot a window"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Record the screen
&lt;/h3&gt;

&lt;p&gt;Using the &lt;code&gt;command&lt;/code&gt; + &lt;code&gt;shift&lt;/code&gt; + &lt;code&gt;5&lt;/code&gt; shortcut, you will see a toolbar appear which allows you to record the entire screen, or just a portion of it. &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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1635623129663%2F4TmBzZsyf.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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1635623129663%2F4TmBzZsyf.png" alt="Mac record video toolbar"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To stop recording, just press the same &lt;code&gt;command&lt;/code&gt; + &lt;code&gt;shift&lt;/code&gt; + &lt;code&gt;5&lt;/code&gt; shortcut again and press the "stop" button. I use this very often in PRs, or when showing my team some early progress of a feature I'm working on.&lt;/p&gt;

&lt;p&gt;You can even trim the beginning and end of the video using this button:&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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1635623459731%2FNDwUc-vkE.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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1635623459731%2FNDwUc-vkE.png" alt="Trim video button on Mac"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Noteworthy screenshot app
&lt;/h3&gt;

&lt;p&gt;I thought it was worth calling out a great free tool which I've found very useful called &lt;a href="https://shottr.cc/" rel="noopener noreferrer"&gt;Shottr&lt;/a&gt;. It hasn't fully replaced the native screenshot functionality for me, but it's mighty close.&lt;/p&gt;

&lt;h2&gt;
  
  
  Shortcuts ⌨️
&lt;/h2&gt;

&lt;p&gt;First of all, the keyboard is subtly different compared to Windows. The &lt;code&gt;command&lt;/code&gt; key is what you'll use for most shortcuts and commands, it's mostly similar to the &lt;code&gt;ctrl&lt;/code&gt; key on Windows. In most cases, shortcuts that require the use of the &lt;code&gt;alt&lt;/code&gt; key on windows will use the &lt;code&gt;option&lt;/code&gt; key on Mac. &lt;/p&gt;

&lt;p&gt;Once I got used to these differences I started to look up various shortcuts for applications I use every day. Below are only some of the shortcuts I use, but &lt;a href="https://support.apple.com/en-us/HT201236" rel="noopener noreferrer"&gt;here&lt;/a&gt; is the full list if you need it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Basics
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Shortcut&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;command&lt;/code&gt; + &lt;code&gt;x&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Cut&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;command&lt;/code&gt; + &lt;code&gt;c&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Copy&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;command&lt;/code&gt; + &lt;code&gt;v&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Paste&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;command&lt;/code&gt; + &lt;code&gt;w&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Close window&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;command&lt;/code&gt; + &lt;code&gt;z&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Undo&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;command&lt;/code&gt; + &lt;code&gt;spacebar&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Open spotlight to quickly find and open apps, documents, and other files.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;command&lt;/code&gt; + &lt;code&gt;tab&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Switch apps&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;command&lt;/code&gt; + &lt;code&gt;n&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;New finder window&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;command&lt;/code&gt; + &lt;code&gt;delete&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Delete file / folder&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;command&lt;/code&gt; + &lt;code&gt;shift&lt;/code&gt; + &lt;code&gt;.&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Show hidden files in Finder&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;control&lt;/code&gt; + &lt;code&gt;down arrow&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Show all windows of the active app (i.e all Chrome windows)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;command&lt;/code&gt; + &lt;code&gt;shift&lt;/code&gt; + &lt;code&gt;3&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Take a screenshot of the currently active screen&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;command&lt;/code&gt; + &lt;code&gt;shift&lt;/code&gt; + &lt;code&gt;4&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Take a screenshot a portion of the screen&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;command&lt;/code&gt; + &lt;code&gt;shift&lt;/code&gt; + &lt;code&gt;4&lt;/code&gt; + &lt;code&gt;space&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Take a sccreenshot of a window of your choosing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;command&lt;/code&gt; + &lt;code&gt;shift&lt;/code&gt; + &lt;code&gt;control&lt;/code&gt; + &lt;code&gt;4&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Take a screenshot of a portion of the screen and copy it to the clipboard&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;command&lt;/code&gt; + &lt;code&gt;c&lt;/code&gt; then &lt;code&gt;command&lt;/code&gt; + &lt;code&gt;option&lt;/code&gt; + &lt;code&gt;v&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Cut and paste files in finder&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;enter&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Rename selected file or folder&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Text editing
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Shortcut&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;command&lt;/code&gt; + &lt;code&gt;left arrow&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Go to beginning of line&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;command&lt;/code&gt; + &lt;code&gt;right arrow&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Go to end of line&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;command&lt;/code&gt; + &lt;code&gt;control&lt;/code&gt; + &lt;code&gt;spacebar&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Show Emoji viewer 😃&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;command&lt;/code&gt; + &lt;code&gt;shift&lt;/code&gt; + &lt;code&gt;v&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Paste without formatting&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;option&lt;/code&gt; + &lt;code&gt;backspace&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Delete word&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;command&lt;/code&gt; + &lt;code&gt;backspace&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Delete all text left of cursor&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Chrome
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Shortcut&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;command&lt;/code&gt; + &lt;code&gt;option&lt;/code&gt; + &lt;code&gt;i&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Open dev tools&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;command&lt;/code&gt; + &lt;code&gt;r&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Reload page&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;command&lt;/code&gt; + &lt;code&gt;f&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Search within the page&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;command&lt;/code&gt; + &lt;code&gt;shift&lt;/code&gt; + &lt;code&gt;t&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Open last closed tab&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Slack
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Shortcut&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;command&lt;/code&gt; + &lt;code&gt;shift&lt;/code&gt; + &lt;code&gt;c&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Format higlighted text to code&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;command&lt;/code&gt; + &lt;code&gt;k&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Quick switcher&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;command&lt;/code&gt; + &lt;code&gt;shift&lt;/code&gt; + &lt;code&gt;u&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Add link text (You can also paste a link directly onto highlighted text to achieve the same)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  iTerm2
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Shortcut&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;control&lt;/code&gt; + &lt;code&gt;u&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Delete line&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;control&lt;/code&gt; + &lt;code&gt;y&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Restore previous deleted line. This is handy if you need to run another command then come back to the previous one.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;command&lt;/code&gt; + &lt;code&gt;t&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;New tab&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  VS Code
&lt;/h3&gt;

&lt;p&gt;For VS Code, I have customised a lot of the shortcuts myself as I personally think they make more sense and are easier to remember, so I've included them below. However, &lt;a href="https://code.visualstudio.com/shortcuts/keyboard-shortcuts-macos.pdf" rel="noopener noreferrer"&gt;here's a list&lt;/a&gt; of the default out-of-the-box shortcuts.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Shortcut&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;command&lt;/code&gt; + &lt;code&gt;click&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Go to definition&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;command&lt;/code&gt; + &lt;code&gt;p&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Quick search&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;option&lt;/code&gt; + &lt;code&gt;left arrow&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Go back&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;option&lt;/code&gt; + &lt;code&gt;right arrow&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Go forward&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;command&lt;/code&gt; + &lt;code&gt;d&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Copy lines down i.e duplicate line of code&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;control&lt;/code&gt; + &lt;code&gt;e&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Add cursor to next matching text&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;command&lt;/code&gt; + &lt;code&gt;/&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Comment out line&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;command&lt;/code&gt; + &lt;code&gt;backspace&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Delete line&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;command&lt;/code&gt; + &lt;code&gt;\&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Open file in side pane&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Final thoughts 🤔
&lt;/h2&gt;

&lt;p&gt;You might wonder if I miss Windows for frontend development, well my answer is "kinda but not really". Yes, Windows does some things better, but Mac has some of its own advantages. I think Mac is a happy medium for software developers who work on a lot of backend code as it's based on Unix, runs most Linux applications but is still user friendly. However, as a frontend developer, all I really need is VS Code and a browser to do my work, both of which work exactly the same regardless of which OS I'm using.&lt;/p&gt;

&lt;p&gt;Hopefully, this article can make the Windows to Mac switch that small bit easier for you. If you found this article useful, or have some tips of your own, feel free to leave a comment 🙌&lt;/p&gt;

&lt;h2&gt;
  
  
  Useful resources 📖
&lt;/h2&gt;

&lt;p&gt;I found these resources useful when learning the basics during my Windows to Mac switcharoo:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://support.apple.com/en-us/HT204216" rel="noopener noreferrer"&gt;Mac tips for Windows switchers&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://support.apple.com/en-gb/guide/mac-help/cpmh0038/mac" rel="noopener noreferrer"&gt;A list of Windows and Mac terms to help you find what you’re looking for.&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Want more? 📢
&lt;/h2&gt;

&lt;p&gt;I mainly write about real-world tech topics I face in my everyday life as a frontend developer. If this appeals to you, then feel free to follow me on Twitter for more: &lt;a href="https://twitter.com/cmacdonnacha" rel="noopener noreferrer"&gt;https://twitter.com/cmacdonnacha&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Bye for now 👋&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>tooling</category>
      <category>beginners</category>
      <category>productivity</category>
    </item>
    <item>
      <title>How to test a select element with React Testing Library</title>
      <dc:creator>Cathal Mac Donnacha 🚀</dc:creator>
      <pubDate>Sun, 10 Oct 2021 15:52:27 +0000</pubDate>
      <link>https://forem.com/cathalmacdonnacha/how-to-test-a-select-element-with-react-testing-library-3e4b</link>
      <guid>https://forem.com/cathalmacdonnacha/how-to-test-a-select-element-with-react-testing-library-3e4b</guid>
      <description>&lt;p&gt;I recently needed to add tests for a &lt;code&gt;&amp;lt;select&amp;gt;&lt;/code&gt; element I was developing, and I couldn't find a lot of resources on how to do this with  &lt;a href="https://testing-library.com/docs/react-testing-library/intro/"&gt;React Testing Library&lt;/a&gt;, so I'll share the approach I went with.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;The &lt;code&gt;&amp;lt;select&amp;gt;&lt;/code&gt; element&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;First of all, let's create a &lt;code&gt;&amp;lt;select&amp;gt;&lt;/code&gt; element with some options. Here I have an array with 3 countries:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;countries&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="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Austria&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;isoCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;AT&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;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;United States&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;isoCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;US&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;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Ireland&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;isoCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;IE&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;Here's the &lt;code&gt;&amp;lt;select&amp;gt;&lt;/code&gt; element itself, it has:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A default placeholder &lt;code&gt;&amp;lt;option&amp;gt;&lt;/code&gt; asking the user to "Select a country".&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;.map&lt;/code&gt; method so we can iterate over the &lt;code&gt;countries&lt;/code&gt; array and add an &lt;code&gt;&amp;lt;option&amp;gt;&lt;/code&gt; element for each one.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;select&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;option&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Select a country&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;option&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;countries&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;country&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;option&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;country&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isoCode&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;country&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isoCode&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;country&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;option&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;select&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Tests
&lt;/h2&gt;

&lt;p&gt;Now that we have a basic &lt;code&gt;&amp;lt;select&amp;gt;&lt;/code&gt; element which displays some countries, let's go ahead and write some tests! Yay...my favourite part 😀&lt;/p&gt;

&lt;p&gt;The beauty of React Testing Library is that it makes you focus more on writing tests the way an actual user would interact with your application, so that's the approach I've taken with the tests below. Of course you may have your own unique scenarios, if you do, just think &lt;em&gt;"How would a real user interact with my select element?"&lt;/em&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Default selection&lt;/strong&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should correctly set default option&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="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;App&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getByRole&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;option&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;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Select a country&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nx"&gt;selected&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toBe&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;strong&gt;Correct number of options&lt;/strong&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should display the correct number of options&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="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;App&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getAllByRole&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;option&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&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;
  
  
  &lt;strong&gt;Change selected option&lt;/strong&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should allow user to change country&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="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;App&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;userEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;selectOptions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="c1"&gt;// Find the select element, like a real user would.&lt;/span&gt;
    &lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getByRole&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;combobox&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="c1"&gt;// Find and select the Ireland option, like a real user would.&lt;/span&gt;
    &lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getByRole&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;option&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;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Ireland&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;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getByRole&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;option&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;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Ireland&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nx"&gt;selected&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toBe&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Gotchas
&lt;/h2&gt;

&lt;p&gt;Initially when I started to look into writing tests for these scenarios I went with the following approach:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should allow user to change country&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="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;App&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;userEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;selectOptions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getByRole&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;combobox&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getByRole&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;option&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;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Ireland&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="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getByRole&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;option&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;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Ireland&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})).&lt;/span&gt;&lt;span class="nx"&gt;toBeInTheDocument&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;Notice the difference? I was only checking that the "Ireland" &lt;code&gt;&amp;lt;option&amp;gt;&lt;/code&gt; existed instead of checking if it was actually selected. Yet my test was still passing 🤔&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getByRole&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;option&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;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Ireland&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})).&lt;/span&gt;&lt;span class="nx"&gt;toBeInTheDocument&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's take a look at why this happened. When the component is loaded, the following is rendered:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;select&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;option&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Select a country&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;option&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;option&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"US"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;United States&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;option&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;option&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"IE"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Ireland&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;option&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;option&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"AT"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Austria&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;option&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;select&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So from JSDOM's point of view, the "Ireland" &lt;code&gt;&amp;lt;option&amp;gt;&lt;/code&gt; &lt;strong&gt;always&lt;/strong&gt; exists within the document, causing my test to pass!&lt;/p&gt;

&lt;p&gt;Whereas the correct approach is to use &lt;code&gt;.selected&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getByRole&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;option&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;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Ireland&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nx"&gt;selected&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toBe&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Gotchas like this can be just as dangerous as not writing the test in the first place as it gives you false confidence about your tests. This is why I always recommend intentionally causing your tests to fail, like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getByRole&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;option&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;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Austria&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nx"&gt;selected&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toBe&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;❌ Test failed: should allow user to change country
Expected: &lt;span class="nb"&gt;true
&lt;/span&gt;Received: &lt;span class="nb"&gt;false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This way you can be confident that it only passes for the intended scenario 🥳&lt;/p&gt;

&lt;h2&gt;
  
  
  Full code example
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://codesandbox.io/s/testing-a-select-element-with-react-testing-library-wtxze?file=/src/App.test.js"&gt;Here's a codesandox&lt;/a&gt; which includes the basic examples shown above. &lt;/p&gt;

&lt;h2&gt;
  
  
  Final thoughts
&lt;/h2&gt;

&lt;p&gt;So there it is, you should now be able to write some basic tests for your &lt;code&gt;&amp;lt;select&amp;gt;&lt;/code&gt; elements using React Testing Library. Of course, I'm not an expert on this topic, I'm simply sharing what I learned in the hope that I can pass on some knowledge.&lt;/p&gt;

&lt;p&gt;If you found this article useful, please give it a like and feel free to leave any feedback in the comments 🙏&lt;/p&gt;

&lt;h2&gt;
  
  
  Want to see more?
&lt;/h2&gt;

&lt;p&gt;I mainly write about real tech topics I face in my everyday life as a Frontend Developer. If this appeals to you then feel free to follow me on Twitter: &lt;a href="https://twitter.com/cmacdonnacha"&gt;https://twitter.com/cmacdonnacha&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Bye for now 👋&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>react</category>
      <category>testing</category>
      <category>beginners</category>
    </item>
  </channel>
</rss>
