<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Krzysztof Sordyl</title>
    <description>The latest articles on DEV Community by Krzysztof Sordyl (@verthon).</description>
    <link>https://dev.to/verthon</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%2F147459%2F03040685-9a71-48b0-82b3-d39bc79a7886.jpeg</url>
      <title>DEV Community: Krzysztof Sordyl</title>
      <link>https://dev.to/verthon</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/verthon"/>
    <language>en</language>
    <item>
      <title>Part 4: Migration to RsBuild</title>
      <dc:creator>Krzysztof Sordyl</dc:creator>
      <pubDate>Sun, 07 Sep 2025 06:08:33 +0000</pubDate>
      <link>https://dev.to/verthon/part-4-migration-to-rsbuild-5368</link>
      <guid>https://dev.to/verthon/part-4-migration-to-rsbuild-5368</guid>
      <description>&lt;p&gt;&lt;strong&gt;Recap of Part 3.&lt;/strong&gt; In the previous episode, we added:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Jest test runner&lt;/li&gt;
&lt;li&gt;CSS with PostCSS&lt;/li&gt;
&lt;li&gt;asset handling&lt;/li&gt;
&lt;li&gt;bundle-size observability&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As the boilerplate grows, so do:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;setup code&lt;/li&gt;
&lt;li&gt;configuration surface&lt;/li&gt;
&lt;li&gt;plugin/tool dependencies&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You could ship this as an npm package (like &lt;code&gt;react-scripts&lt;/code&gt; / &lt;code&gt;next-scripts&lt;/code&gt;) to hide bundler complexity—but that requires:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ongoing maintenance and versioning&lt;/li&gt;
&lt;li&gt;an internal abstraction for bundler setup&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Alternative
&lt;/h2&gt;

&lt;p&gt;If we proceed with Webpack in this part, we’ll most likely:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;migrate Babel → SWC for faster transforms&lt;/li&gt;
&lt;li&gt;migrate Jest → Vitest&lt;/li&gt;
&lt;li&gt;enable source maps&lt;/li&gt;
&lt;li&gt;tune chunk splitting&lt;/li&gt;
&lt;li&gt;explore SSR support&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Instead, this part migrates the boilerplate to &lt;strong&gt;Rsbuild&lt;/strong&gt; and evaluates the trade-offs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why Rsbuild?
&lt;/h3&gt;

&lt;p&gt;Powered by &lt;strong&gt;Rspack&lt;/strong&gt; (Webpack-compatible APIs) with a “batteries-included” DX similar to Vite.&lt;br&gt;
It’s faster than Webpack and needs less boilerplate.&lt;/p&gt;

&lt;h3&gt;
  
  
  Vite
&lt;/h3&gt;

&lt;p&gt;Vite is a go-to modern build tool. It isn’t part of this comparison because an apples-to-apples (Rust vs Rust) test would use Vite + Rolldown, which isn’t fully stabilized yet.&lt;br&gt;
Also, &lt;strong&gt;Rspack&lt;/strong&gt; (the bundler inside Rsbuild) exposes Webpack-compatible APIs, so migrating from Webpack to Rsbuild is typically smoother than switching to Vite.&lt;/p&gt;

&lt;p&gt;You can see direct benchmarks here: &lt;strong&gt;&lt;a href="https://github.com/rspack-contrib/build-tools-performance" rel="noopener noreferrer"&gt;Rspack benchmarks&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Establishing comparison metrics
&lt;/h2&gt;

&lt;p&gt;To compare two tools we need a set of metrics:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;bundle size&lt;/li&gt;
&lt;li&gt;build time (local and CI)&lt;/li&gt;
&lt;li&gt;dev-server readiness time&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;package.json&lt;/code&gt; dependency count&lt;/li&gt;
&lt;li&gt;overall project size on the machine&lt;/li&gt;
&lt;li&gt;install time of the project dependencies (with existing &lt;code&gt;node_modules&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;boilerplate lines of code (without &lt;code&gt;node_modules&lt;/code&gt;, &lt;code&gt;package-lock.json&lt;/code&gt;)&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Environment controls
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Hardware: MacBook Air M1 (local), GitHub Actions free tier (CI)&lt;/li&gt;
&lt;li&gt;Node &amp;amp; package manager: Node 22 LTS (22.19.0), installs with existing &lt;code&gt;node_modules&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Sampling: 5 runs for time-related metrics&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Tooling:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;timing: &lt;code&gt;time&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;bundle: stat size and gzipped bundle&lt;/li&gt;
&lt;li&gt;size: &lt;code&gt;du -sh&lt;/code&gt; (project); &lt;code&gt;jq&lt;/code&gt; for &lt;code&gt;.dependencies&lt;/code&gt; and &lt;code&gt;devDependencies&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;dev-server readiness: time printed by the bundler when the server is ready&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;ℹ️ I’ll create another post with comparisons on larger, real-world apps.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Webpack results
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;bundle size: &lt;strong&gt;199.96 KB&lt;/strong&gt; (&lt;strong&gt;52.2 KB&lt;/strong&gt; gzipped)&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;build time (local and CI):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;local: AVG &lt;strong&gt;3.614s&lt;/strong&gt;, MED &lt;strong&gt;3.574s&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;CI: AVG &lt;strong&gt;6.418s&lt;/strong&gt;, MED &lt;strong&gt;6.387s&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;dev-server readiness time: AVG &lt;strong&gt;1.024s&lt;/strong&gt;, MED &lt;strong&gt;1.023s&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;code&gt;package.json&lt;/code&gt; dependency count:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;dependencies: &lt;strong&gt;3&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;devDependencies: &lt;strong&gt;31&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;overall project size on the machine: &lt;strong&gt;212 MB&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;install time of project dependencies (with existing &lt;code&gt;node_modules&lt;/code&gt;):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;local: AVG &lt;strong&gt;1.556s&lt;/strong&gt;, MED &lt;strong&gt;1.527s&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;CI: AVG &lt;strong&gt;10.981s&lt;/strong&gt;, MED &lt;strong&gt;11.890s&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;boilerplate lines of code: &lt;strong&gt;285&lt;/strong&gt; overall&lt;/p&gt;&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Rsbuild results
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;bundle size: &lt;strong&gt;143.62 KB&lt;/strong&gt; (&lt;strong&gt;45.62 KB&lt;/strong&gt; gzipped) — smaller due to dropping unused core-js polyfills&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;build time (local and CI):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;local: AVG &lt;strong&gt;0.865s&lt;/strong&gt;, MED &lt;strong&gt;0.932s&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;CI: AVG &lt;strong&gt;1.246s&lt;/strong&gt;, MED &lt;strong&gt;1.242s&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;dev-server readiness time: AVG: &lt;strong&gt;0.038s&lt;/strong&gt;, MED: &lt;strong&gt;0.040s&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;code&gt;package.json&lt;/code&gt; dependency count:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;dependencies: &lt;strong&gt;3&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;devDependencies: &lt;strong&gt;16&lt;/strong&gt; (15 fewer)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;overall project size on the machine: &lt;strong&gt;216 MB&lt;/strong&gt; (higher due to Rsbuild cache/binaries)&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;install time of project dependencies (with existing &lt;code&gt;node_modules&lt;/code&gt;):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;local: AVG &lt;strong&gt;1.358s&lt;/strong&gt;, MED &lt;strong&gt;1.308s&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;CI: AVG &lt;strong&gt;8.527s&lt;/strong&gt;, MED &lt;strong&gt;8.622s&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;boilerplate lines of code: &lt;strong&gt;256&lt;/strong&gt; overall&lt;/p&gt;&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;It’s expected that a Webpack + Babel setup lags on raw performance.&lt;br&gt;
Migration also improves business metrics, such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;faster hotfix recovery, less downtime cost&lt;/li&gt;
&lt;li&gt;faster releases, shorter time-to-market, ship value sooner&lt;/li&gt;
&lt;li&gt;lower infra spend (at org scale)&lt;/li&gt;
&lt;li&gt;fewer deps / simpler pipeline → less maintenance time, fewer failures/reruns&lt;/li&gt;
&lt;li&gt;smaller bundles → cheaper bandwidth/CDN, faster pages → better conversion potential&lt;/li&gt;
&lt;li&gt;faster dev server/local builds/HMR → less idle/wait time for engineers&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Trade-offs
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;doesn’t have as large a plugin ecosystem as Webpack&lt;/li&gt;
&lt;li&gt;Rspack’s design goal is not 100% compliance with the &lt;a href="https://rspack.rs/misc/faq#is-rspack-100-compatible-with-webpack-api" rel="noopener noreferrer"&gt;Webpack API&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Rspack won’t create identical &lt;a href="https://rspack.rs/misc/faq#can-rspack-be-used-in-production-environments-now-what-are-the-differences-between-production-artifacts-and-webpack" rel="noopener noreferrer"&gt;artifacts to Webpack&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;doesn’t support &lt;a href="https://rspack.rs/misc/faq#does-rspack-plan-to-support-wasm-for-browser-side-build" rel="noopener noreferrer"&gt;WASM for browser-side builds&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That concludes this part. Full diff in the PR: &lt;a href="https://github.com/Verthon/webpack-react-boilerplate/pull/6/files" rel="noopener noreferrer"&gt;https://github.com/Verthon/webpack-react-boilerplate/pull/6/files&lt;/a&gt;&lt;br&gt;
Best one-line summary of the PR: removed-to-added code ratio &lt;strong&gt;&lt;code&gt;+930 / −5,721&lt;/code&gt;&lt;/strong&gt;.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>webpack</category>
      <category>performance</category>
      <category>react</category>
    </item>
    <item>
      <title>Part 3: Asset Handling, Unit Testing, and Bundle Analysis</title>
      <dc:creator>Krzysztof Sordyl</dc:creator>
      <pubDate>Sun, 14 Apr 2024 08:40:12 +0000</pubDate>
      <link>https://dev.to/verthon/part-3-asset-handling-unit-testing-and-bundle-analysis-3hjd</link>
      <guid>https://dev.to/verthon/part-3-asset-handling-unit-testing-and-bundle-analysis-3hjd</guid>
      <description>&lt;p&gt;&lt;strong&gt;Recap of Part 2&lt;/strong&gt;: In the previous installment, we integrated Babel and React into our Webpack setup, enhancing our modern SPA boilerplate.&lt;br&gt;
We configured Babel to support the latest JavaScript features, TypeScript, and React JSX.&lt;br&gt;
Additionally, we implemented environment variable management using dotenv and refined our Webpack configuration for handling .tsx files.&lt;/p&gt;
&lt;h3&gt;
  
  
  Unit testing setup
&lt;/h3&gt;

&lt;p&gt;For unit tests we will use Jest which is one of most popular solutions on the market.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Install following dev-dependencies&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i &lt;span class="nt"&gt;-D&lt;/span&gt; jest @types/jest @swc/core @swc/jest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Jest configuration file&lt;/strong&gt; Create &lt;code&gt;jest.config.js&lt;/code&gt; in the root of the project.&lt;br&gt;
Following config will tell jest to transform &lt;code&gt;ts|js|tsx|jsx&lt;/code&gt; files with &lt;code&gt;swc-jest&lt;/code&gt;&lt;br&gt;
&lt;strong&gt;Note:&lt;/strong&gt; swc-jest does not perform type checking in your tests.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// jest.config.js&lt;/span&gt;
&lt;span class="cm"&gt;/** @type {import('@jest/types').Config.InitialOptions} */&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;^.+&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;.(t|j)sx?$&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;@swc/jest&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;resetMocks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;setupFilesAfterEnv&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;&amp;lt;rootDir&amp;gt;/testSetup.ts&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;verbose&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="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="nx"&gt;config&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Environment Configuration for Testing&lt;/strong&gt;&lt;br&gt;
Introduce an &lt;code&gt;env.test&lt;/code&gt; file to manage test-specific environment variables:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;//.env.test

ENVIRONMENT_NAME=test
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Setting Up Environment Variables&lt;/strong&gt;: Configure a setup file to load the appropriate environment variables before all 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="c1"&gt;// test.setup.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;loadEnvs&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;./config/env&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="nf"&gt;beforeAll&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;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NODE_ENV&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;test&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

  &lt;span class="nf"&gt;loadEnvs&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;Verify Environment Configuration&lt;/strong&gt;&lt;br&gt;
In order to confirm the behaviour of loading test envs we could create a dummy 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="c1"&gt;// src/example.spec.ts&lt;/span&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;example unit test&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;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 have the NODE_ENV equal to test&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;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NODE_ENV&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;test&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;&lt;strong&gt;Commands for Running Tests&lt;/strong&gt;: Update &lt;code&gt;package.json&lt;/code&gt; with commands to run and clear the Jest cache:&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;"test:unit"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"jest"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"test:unit:clear"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"jest --clearCache"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the tests can be executed by typing: &lt;code&gt;npm run test:unit&lt;/code&gt; in the terminal&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key points&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the Jest setup is tailored for TypeScript files within the Node.js environment (Jest's default setting)&lt;/li&gt;
&lt;li&gt;the setup can be easily extended with &lt;a href="https://testing-library.com/docs/react-testing-library/intro/"&gt;React Testing Library&lt;/a&gt; to cover component tests as well, however it is good to explore alternatives first 👇&lt;/li&gt;
&lt;li&gt;especially tools that allow testing in the browser like:

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.cypress.io/guides/component-testing/overview"&gt;Cypress Component Testing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://storybook.js.org/docs/writing-tests"&gt;Storybook Testing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://netflixtechblog.com/introducing-safetest-a-novel-approach-to-front-end-testing-37f9f88c152d"&gt;SafeTest&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://playwright.dev/docs/test-components"&gt;Playright Component Testing&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;from my experience in large applications developers often encounter performance issues with React Testing Library due to the rendering of extensive DOM trees and scanning through it.&lt;/li&gt;
&lt;li&gt;additionally in heavy integration tests Playright or Cypress combined with &lt;a href="https://mswjs.io/docs/integrations/node"&gt;msw&lt;/a&gt; for network call interception, tend to perform more efficiently&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Asset handling
&lt;/h3&gt;

&lt;p&gt;Webpack simplifies the management of static assets, all we need to do is to extend &lt;code&gt;webpack.config.ts&lt;/code&gt; - module.rules&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;// webpack.config.ts&lt;/span&gt;
&lt;span class="kr"&gt;module&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&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;// ... rest of the previous config&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\.(&lt;/span&gt;&lt;span class="sr"&gt;png|svg|jpg|jpeg|gif&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="sr"&gt;$/i&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;asset/resource&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="na"&gt;test&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\.(&lt;/span&gt;&lt;span class="sr"&gt;woff|woff2|eot|ttf|otf&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="sr"&gt;$/i&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;asset/resource&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;code&gt;asset/resource&lt;/code&gt; will emit the file in the production bundle, this is beneficial because:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;it won't bloat JavaScript bundle size&lt;/li&gt;
&lt;li&gt;browsers can cache each asset independently&lt;/li&gt;
&lt;li&gt;emited file will include hash and filename for cache-busting&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  CSS setup
&lt;/h3&gt;

&lt;p&gt;Frontend applications are incomplete without styling. To efficiently manage CSS files within a Webpack build, we need to update config.&lt;/p&gt;

&lt;p&gt;Install following dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i &lt;span class="nt"&gt;-D&lt;/span&gt; style-loader css-loader postcss-loader mini-css-extract-plugin css-minimizer-webpack-plugin postcss postcss-preset-env autoprefixer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;style-loader: Injects CSS into the DOM via &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; tags, useful during development for hot module replacement.&lt;/li&gt;
&lt;li&gt;css-loader: Resolves @import and url() within CSS files as module dependencies, enabling them to be bundled by Webpack.&lt;/li&gt;
&lt;li&gt;postcss-loader: Integrates PostCSS into the build process, allowing for CSS transformations using plugins.&lt;/li&gt;
&lt;li&gt;MiniCssExtractPlugin: Extracts CSS into separate files, ideal for production builds to leverage browser caching.&lt;/li&gt;
&lt;li&gt;CssMinimizerPlugin: Optimizes and minifies CSS, enhancing load times and efficiency in production.&lt;/li&gt;
&lt;li&gt;postcss: Facilitates the use of advanced CSS through plugins like autoprefixer and postcss-preset-env.&lt;/li&gt;
&lt;li&gt;postcss-preset-env: Allows the use of future CSS features by compiling them to compatible code today.&lt;/li&gt;
&lt;li&gt;autoprefixer: Automatically applies vendor prefixes to CSS rules, ensuring cross-browser compatibility based on "Can I Use" data.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Create dedicated file for post-css configuration to not bloat Webpack config file&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="cm"&gt;/**
 * @type {import('postcss').ProcessOptions}
 */&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;postcss-preset-env&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;autoprefixer&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;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="nx"&gt;config&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lastly the webpack config has to be updated with loaders that were installed previously to handle CSS in JS files.&lt;br&gt;
In &lt;code&gt;module.rules&lt;/code&gt; add yet another loaders for css and post-css.&lt;br&gt;
&lt;strong&gt;Note&lt;/strong&gt; We only want to extract the CSS to dedicated files on production build.&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;// webpack.config.ts&lt;/span&gt;
&lt;span class="kr"&gt;module&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&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;// ... rest of the previous config&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;css$/i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;use&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="nx"&gt;mode&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;production&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;MiniCssExtractPlugin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;loader&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;style-loader&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;css-loader&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;postcss-loader&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Additionally new plugins has to be initialized in the &lt;code&gt;plugins&lt;/code&gt; array. Additionally we want to run those optimizations only on production builds.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// webpack.config.ts
import MiniCssExtractPlugin from "mini-css-extract-plugin";
import CssMinimizerPlugin from "css-minimizer-webpack-plugin";

plugins: [
  mode === "production" &amp;amp;&amp;amp; new MiniCssExtractPlugin(),
    mode === "production" &amp;amp;&amp;amp; new CssMinimizerPlugin(),
]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Bundle size observability
&lt;/h3&gt;

&lt;p&gt;Applications bundle size tend to grow really fast, it is good idea to observe frequently how much our chunks weight.&lt;br&gt;
Webpack offers advanced plugin to visualize bundle size in a graphical format.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Install dependencies&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i &lt;span class="nt"&gt;-D&lt;/span&gt; webpack-bundle-analyzer @types/webpack-bundle-analyzer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Optimize build commands&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;regular build, which is faster and can run without issues in the pipelines&lt;/li&gt;
&lt;li&gt;build-analyze for devs to check the bundle size using &lt;code&gt;webpack-bundle-analyzer&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Add the following command to your &lt;code&gt;package.json&lt;/code&gt;, utilizing the &lt;code&gt;ANALYZE_BUILD&lt;/code&gt; environment variable to toggle the analysis mode:&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;"build:analyze"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ANALYZE_BUILD=true npm run build"&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;&lt;strong&gt;Configure Webpack&lt;/strong&gt;:&lt;br&gt;
Modify your Webpack configuration to conditionally include the BundleAnalyzerPlugin based on the ANALYZE_BUILD flag.&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;// webpack.config.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;BundleAnalyzerPlugin&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;webpack-bundle-analyzer&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;createBundleAnalyzerConfig&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;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;isAnalyzeMode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ANALYZE_BUILD&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="kd"&gt;const&lt;/span&gt; &lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;BundleAnalyzerPlugin&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;opts&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="nx"&gt;isAnalyzeMode&lt;/span&gt;
        &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;analyzerMode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;server&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="na"&gt;analyzerMode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;disabled&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;config&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;bundleAnalyzerConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createBundleAnalyzerConfig&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// in the createWebpackConfig function&lt;/span&gt;
&lt;span class="nl"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="c1"&gt;//... rest of the config&lt;/span&gt;

  &lt;span class="nx"&gt;mode&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;production&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;BundleAnalyzerPlugin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bundleAnalyzerConfig&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;Execution&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Run &lt;code&gt;npm run build&lt;/code&gt; for a standard build, suitable for deployment and CI environments like GitHub Actions.&lt;/li&gt;
&lt;li&gt;Use npm run &lt;code&gt;build:analyze&lt;/code&gt; to launch a server that provides a detailed breakdown of the bundle, enabling developers to visually assess which parts are contributing most to the size.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This setup ensures that developers have the tools needed to maintain optimal performance and manageability of application bundles efficiently.&lt;/p&gt;

&lt;p&gt;That concludes this part. You can review all the details in following PR: &lt;a href="https://github.com/Verthon/webpack-react-boilerplate/pull/3/files"&gt;https://github.com/Verthon/webpack-react-boilerplate/pull/3/files&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thank you, any feedback is highly appreciated.&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>webpack</category>
      <category>webdev</category>
      <category>typescript</category>
    </item>
    <item>
      <title>How I migrated from CRA to RSBuild</title>
      <dc:creator>Krzysztof Sordyl</dc:creator>
      <pubDate>Sat, 13 Jan 2024 09:14:30 +0000</pubDate>
      <link>https://dev.to/verthon/how-i-migrated-from-cra-to-rsbuild-4ia8</link>
      <guid>https://dev.to/verthon/how-i-migrated-from-cra-to-rsbuild-4ia8</guid>
      <description>&lt;p&gt;I recently revisited an old personal React project and discovered it was built using Create React App (CRA). &lt;br&gt;
While considering a switch to Vite for its performance benefits, I stumbled upon RSBuild, which, according to its documentation, is even faster: &lt;a href="https://rsbuild.dev/guide/start/#-performance" rel="noopener noreferrer"&gt;RSBuild Performance&lt;/a&gt;.&lt;/p&gt;

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

&lt;p&gt;Here are my key reasons for choosing RSBuild:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Performance&lt;/strong&gt;: It's designed for speed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CRA is Outdated&lt;/strong&gt;: The last CRA release was on &lt;strong&gt;&lt;a href="https://github.com/facebook/create-react-app/releases/tag/v5.0.1" rel="noopener noreferrer"&gt;April 12, 2022&lt;/a&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Migration Guide&lt;/strong&gt;: RSBuild provides a detailed guide for seamless migration.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Batteries included&lt;/strong&gt;: I don’t need to worry about additional integration, RSBuild covers what I need&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Few words about RSBuild
&lt;/h2&gt;

&lt;p&gt;RSBuild is based on RSPack, a Rust-written bundler optimized for web development. It's user-friendly and minimizes configuration needs.&lt;/p&gt;

&lt;p&gt;Being framework-agnostic, RSBuild offers a robust plugin system. For React integration, I simply added &lt;code&gt;plugins: [pluginReact()],&lt;/code&gt; in &lt;code&gt;rsbuild.config.ts&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;RSBuild's comprehensive documentation includes examples and detailed guides for migrating from:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Webpack&lt;/li&gt;
&lt;li&gt;Create React App (CRA)&lt;/li&gt;
&lt;li&gt;Vue CLI&lt;/li&gt;
&lt;li&gt;Modern Builder&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  CSS
&lt;/h2&gt;

&lt;p&gt;In my CRA project, I utilized &lt;code&gt;css-modules&lt;/code&gt;. Fortunately, RSBuild supports it out of the box, so no extra effort was required on my part.&lt;/p&gt;

&lt;h2&gt;
  
  
  Environment Variables
&lt;/h2&gt;

&lt;p&gt;My project only had two environments, and I wanted to move away from the &lt;code&gt;REACT_APP&lt;/code&gt; prefix. RSBuild allowed me to easily switch to using &lt;code&gt;PUBLIC&lt;/code&gt; as the prefix, treating all my environment variables as public, which suited my project's needs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing
&lt;/h2&gt;

&lt;p&gt;RSBuild doesn't integrate testing frameworks directly. Thus, there's no straight swap for &lt;code&gt;react-scripts test&lt;/code&gt;. I set up Jest independently. To manage this, I used a separate &lt;code&gt;.env.test&lt;/code&gt; file for test configurations. To avoid additional tooling with &lt;code&gt;ts-node&lt;/code&gt;, I relied on standard CJS files:&lt;/p&gt;

&lt;p&gt;I have stick with good old CJS files:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The first file is for loading environment variables:
```javascript
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;// src/config/loadEnvs.js&lt;/p&gt;

&lt;p&gt;const dotenv = require('dotenv')&lt;br&gt;
const path = require('path')&lt;/p&gt;

&lt;p&gt;const loadEnvs = (mode) =&amp;gt; {&lt;br&gt;
  const testOptions = mode === 'test' &amp;amp;&amp;amp; {&lt;br&gt;
    path: path.resolve(process.cwd(), '.env.test'),&lt;br&gt;
  }&lt;/p&gt;

&lt;p&gt;try {&lt;br&gt;
    return dotenv.config(testOptions)&lt;br&gt;
  } catch (err) {&lt;br&gt;
    console.log(err, 'Missing or wrong env files')&lt;br&gt;
  }&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;module.exports = { loadEnvs }&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
- The second file is used to load the appropriate environment variables and run Jest with arguments specified in package.json:

```javascript


const { loadEnvs } = require('./loadEnvs')

loadEnvs('test')

const argv = process.argv.slice(2)

const jest = require('jest')

jest.run(argv)


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

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;Finally, I updated the test commands in package.json to align with these changes.&lt;/li&gt;
&lt;/ul&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;p&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;loadEnvs&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./loadEnvs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;span class="nf"&gt;loadEnvs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;test&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;argv&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&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;/p&gt;

&lt;p&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;jest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;jest&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;span class="nx"&gt;jest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;/p&gt;

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

&lt;/div&gt;
&lt;h2&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  Results&lt;br&gt;
&lt;/h2&gt;

&lt;p&gt;Overall the migration took me around 30 minutes, which is quite fast in my opinion - even fact that my app is rather small one. &lt;br&gt;
The docs are great I have easily found, what I needed, big shoutout to the RSBuild Team ❤️&lt;/p&gt;

&lt;p&gt;The performance improvements with RSBuild were huge:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Local build time: Reduced to about &lt;strong&gt;0.3 seconds&lt;/strong&gt; 🔥.&lt;/li&gt;
&lt;li&gt;GitHub CI: Reduced to about &lt;strong&gt;0.8 seconds&lt;/strong&gt;, which is incredibly fast 🔥.&lt;/li&gt;
&lt;li&gt;Local development server readiness: Around &lt;strong&gt;0.25 seconds&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxlv2vnw8gqrjcbvrs6rl.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxlv2vnw8gqrjcbvrs6rl.png" alt="RSBuild took 0.8s to build project on CI"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In contrast, with CRA:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Local build time: Typically &lt;strong&gt;7 to 9 seconds&lt;/strong&gt;. &lt;/li&gt;
&lt;li&gt;GitHub CI: Around &lt;strong&gt;26 seconds&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Local development server readiness: Between &lt;strong&gt;3 to 5 seconds&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3rwj1a80nl8y17oo2x5i.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3rwj1a80nl8y17oo2x5i.png" alt="CRA took 26 seconds to build project on CI"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Other migration cases
&lt;/h2&gt;

&lt;p&gt;I also explored other developers' experiences with migrating to RSBuild, as shared on Twitter:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://twitter.com/acemarke/status/1741298626628861992" rel="noopener noreferrer"&gt;https://twitter.com/acemarke/status/1741298626628861992&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://twitter.com/ScriptedAlchemy/status/1741058988094230528" rel="noopener noreferrer"&gt;https://twitter.com/ScriptedAlchemy/status/1741058988094230528&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>webdev</category>
      <category>react</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Part 2: Integrating Babel and React</title>
      <dc:creator>Krzysztof Sordyl</dc:creator>
      <pubDate>Sun, 31 Dec 2023 09:43:17 +0000</pubDate>
      <link>https://dev.to/verthon/building-a-modern-react-spa-boilerplate-with-webpack-5-part-2-integrating-babel-and-react-281c</link>
      <guid>https://dev.to/verthon/building-a-modern-react-spa-boilerplate-with-webpack-5-part-2-integrating-babel-and-react-281c</guid>
      <description>&lt;p&gt;&lt;strong&gt;Recap of Part 1&lt;/strong&gt;: In the first installment of this series, we laid the groundwork for our modern React SPA boilerplate using Webpack 5. &lt;br&gt;
We covered the basics of Webpack, including core &lt;br&gt;
concepts like entry points, outputs, loaders, and plugins. We also set &lt;br&gt;
up our npm environment, ensuring consistency with Node v20 LTS, and &lt;br&gt;
introduced a basic TypeScript configuration. &lt;br&gt;
With a foundational Webpack setup in place, we're now ready to dive into integrating Babel, Webpack Dev Server and React to enhance our SPA's capabilities.&lt;/p&gt;
&lt;h2&gt;
  
  
  Babel setup
&lt;/h2&gt;

&lt;p&gt;In order to use latest JavaScript features, while supporting older browsers, we need to use transpiler. In this configuration step, we will use babel.&lt;/p&gt;

&lt;p&gt;Install following dev-dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i &lt;span class="nt"&gt;-D&lt;/span&gt; babel-loader @babel/preset-typescript @babel/preset-react @babel/preset-env @babel/core
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since preset-env requires core-js we need to install it as well&lt;br&gt;
&lt;/p&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;core-js@3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Additionally we need to specify what browsers need to be supported. You can create the required file with following command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"&amp;gt; 0.25%&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;not dead"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; .browserslistrc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Last but not least to finalize babel setup we would need to create &lt;code&gt;babel.config.js&lt;/code&gt; file with following presets:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;@babel/preset-env&lt;/code&gt; - simply allow to use latest JS syntax, without worry about browser support&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@babel/preset-typescript&lt;/code&gt; - project uses TypeScript, so we need it&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@babel/preset-react&lt;/code&gt; - allows Babel to parse JSX and other React related stuff&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Final config file looks like this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;//babel.config.js&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * @type {import('@babel/core').TransformOptions}
 */&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;presets&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@babel/preset-env&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;useBuiltIns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;usage&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;corejs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&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="s2"&gt;3.35&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;shippedProposals&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@babel/preset-typescript&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@babel/preset-react&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;runtime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;automatic&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;development&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NODE_ENV&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;production&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;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="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we need to tell Webpack how to handle tsx and ts files, current Webpack config need to be extended with following rules:&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="kr"&gt;module&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nl"&gt;rules&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\.(&lt;/span&gt;&lt;span class="sr"&gt;js|mjs|ts|tsx&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="sr"&gt;$/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="na"&gt;include&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cwd&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&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;loader&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;babel-loader&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="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nl"&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="s2"&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="s2"&gt;.tsx&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;.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This allow to pass all the files (tsx, ts, js) in src directory through babel and apply changes to use it safely in the browser.&lt;/p&gt;

&lt;h2&gt;
  
  
  Environment variables
&lt;/h2&gt;

&lt;p&gt;To clear any confusion around envs on the frontend, let's clarify one thing: every env listed in .env.prod will be publicly available. If you need a way to hide some sort of API_KEY - you need most likely a backend proxy layer.&lt;/p&gt;

&lt;p&gt;With public nature of envs on frontend world, lets dive into implementation. The idea is to provide our envs and build time, so when application is being deployed the envs will be available in the runtime. Without it we would end up with runtime error: &lt;code&gt;process is no defined&lt;/code&gt; as it is part of Node.js not browser runtime.&lt;/p&gt;

&lt;p&gt;The whole mechanism is easily extendable for adding new environments (staging, test or whatever you want) - but for now we will stick with basic setup for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;production&lt;/li&gt;
&lt;li&gt;local&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With that in mind lets create 3 files:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;.env.prod - envs for production&lt;/li&gt;
&lt;li&gt;.env.local - envs for local development &lt;strong&gt;Note&lt;/strong&gt;: it must be added to .gitignore to not expose it&lt;/li&gt;
&lt;li&gt;.env.example - example envs structure that matches .env.prod - new dev can easily copy it and fill with proper values&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For now we can add only one variable called for instance &lt;code&gt;ENVIRONMENT_NAME&lt;/code&gt; for simplicity it cane be either &lt;code&gt;prod&lt;/code&gt; or &lt;code&gt;local&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Next install package &lt;code&gt;dotenv&lt;/code&gt; which will help us manage the environment variables:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i &lt;span class="nt"&gt;-D&lt;/span&gt; dotenv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once we have the envs and dotenv installed, we need to tell Webpack to add envs to code at build time. We need some helpers functions so it would be good idea to defined them under &lt;code&gt;config/env.ts&lt;/code&gt; with following content:&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="nx"&gt;dotenv&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dotenv&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;path&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;path&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;EnvironmentVariable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Record&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&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;&amp;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;loadEnvs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;EnvironmentVariable&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;environment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NODE_ENV&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;prod&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;envPath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cwd&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="s2"&gt;`.env.&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;dotenv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;config&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;envPath&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;result&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="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Failed to load .env file: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;envPath&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;result&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="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;result&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="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parsed&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="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mapEnvsToConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;envs&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;key&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="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}):&lt;/span&gt; &lt;span class="nx"&gt;EnvironmentVariable&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;return&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;envs&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;reduce&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;EnvironmentVariable&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;prev&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&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;prev&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;`process.env.&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;envs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;next&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;prev&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;Function loadEnvs is getting proper env file based on NODE_ENV environment variable and creates config using &lt;code&gt;dotenv&lt;/code&gt; package.&lt;/p&gt;

&lt;p&gt;However we still need to provide envs to Webpack in proper format, this task is handled by &lt;code&gt;mapEnvsToConfig&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Last step is to import those functions to webpack config and provide NODE_ENV for build npm script:&lt;/p&gt;

&lt;p&gt;Webpack&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="c1"&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;loadEnvs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;mapEnvsToConfig&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./config/env&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;createWebpackConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Configuration&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;mode&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]):&lt;/span&gt; &lt;span class="nx"&gt;Configuration&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;// load envs&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;envs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;loadEnvs&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;envsConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;mapEnvsToConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;envs&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="na"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cwd&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;src/index.ts&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="na"&gt;module&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\.(&lt;/span&gt;&lt;span class="sr"&gt;js|mjs|ts|tsx&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="sr"&gt;$/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="na"&gt;include&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cwd&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&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;loader&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;babel-loader&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;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;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="s2"&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="s2"&gt;.tsx&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;.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="na"&gt;output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cwd&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dist&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="na"&gt;clean&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="c1"&gt;// pass them to Webpack so that process.env.ENVIRONMENT_NAME won't be undefined&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="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;DefinePlugin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;envsConfig&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;Build script - we still need to pass NODE_ENV&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;"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;"webpack --config scripts/build.ts --node-env=prod"&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;h2&gt;
  
  
  React
&lt;/h2&gt;

&lt;p&gt;Install React&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i react react-dom
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And types for React&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i &lt;span class="nt"&gt;-D&lt;/span&gt; @types/react-dom @types/react
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since we are working on SPA add &lt;code&gt;index.html&lt;/code&gt; inside of new folder &lt;code&gt;public&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Example content of the &lt;code&gt;index.html&lt;/code&gt; can be as follow&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="c"&gt;&amp;lt;!-- index.html --&amp;gt;&lt;/span&gt;

&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"en"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"UTF-8"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;http-equiv=&lt;/span&gt;&lt;span class="s"&gt;"X-UA-Compatible"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"IE=edge"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"viewport"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"width=device-width, initial-scale=1.0"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Webpack React Boilerplate&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;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="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Additionally we need to tell TypeScript that we are using JSX, add following line to &lt;code&gt;compilerOptions&lt;/code&gt; of &lt;code&gt;tsconfig.json&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="s2"&gt;"jsx"&lt;/span&gt;: &lt;span class="s2"&gt;"react-jsx"&lt;/span&gt;,
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next we need dummy react component and bootstrap to mount application in root&lt;/p&gt;

&lt;p&gt;Dummy app&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;// src/App.tsx

&lt;span class="nb"&gt;export &lt;/span&gt;const App &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &amp;lt;p&amp;gt;Hello World&amp;lt;/p&amp;gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/bootstrap.tsx&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;createRoot&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react-dom/client&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;App&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./App&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;render&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;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;container&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;root&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;container&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;root&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createRoot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;container&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;root&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&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="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using render function allows us to have flexibility to pass some configuration to it, for instance when you switch from build-time envs to runtime.&lt;/p&gt;

&lt;p&gt;Last we need to update the &lt;code&gt;src/index.ts&lt;/code&gt; to address changes done in bootstrap&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;// src/index.ts

import&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'./bootstrap'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nb"&gt;export&lt;/span&gt; &lt;span class="o"&gt;{}&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally we will setup the dev-server to be able spin up the development environment. &lt;/p&gt;

&lt;p&gt;Install dev-server package and html plugin&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i &lt;span class="nt"&gt;-D&lt;/span&gt; webpack-dev-server html-webpack-plugin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In order to include webpack bundles automatically as script tags in index.html we need to add HTML plugin with some configuration&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="c1"&gt;//webpack.config.ts&lt;/span&gt;

&lt;span class="nx"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;DefinePlugin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;envsConfig&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="c1"&gt;// new plugin for dealing with HTML&lt;/span&gt;
            &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;HtmlWebpackPlugin&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
                &lt;span class="na"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cwd&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;public/index.html&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;Next we need function that encapsulate the creating dev-server setup create following file &lt;code&gt;config/devServer.ts&lt;/code&gt; which will have that config 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="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;path&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;WebpackConfiguration&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;webpack-dev-server&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;defaultPort&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PORT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;8080&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;createDevServerConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;WebpackConfiguration&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;devServer&lt;/span&gt;&lt;span class="dl"&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="na"&gt;historyApiFallback&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;disableDotRule&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;client&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;overlay&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;logging&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;info&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;static&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;directory&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cwd&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;public&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="na"&gt;serveIndex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;watch&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="na"&gt;liveReload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;hot&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;open&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;defaultPort&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can define PORT environment variable if you need more flexibility otherwise it will use default &lt;code&gt;8080&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Generate the dev-server only for dev mode in webpack config&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;resolve&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nl"&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="s2"&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="s2"&gt;.tsx&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;.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="c1"&gt;// use it only for dev mode&lt;/span&gt;
        &lt;span class="p"&gt;...(&lt;/span&gt;&lt;span class="nx"&gt;mode&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;development&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;devServer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;createDevServerConfig&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="nx"&gt;output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nl"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cwd&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dist&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="nx"&gt;publicPath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nx"&gt;clean&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;Last but not least we need new &lt;code&gt;scripts/start.ts&lt;/code&gt; to be able to use it in npm scripts like build command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;createWebpackConfig&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../webpack.config&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default &lt;/span&gt;&lt;span class="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;return&lt;/span&gt; &lt;span class="nf"&gt;createWebpackConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;development&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;Additionally it has to be specified in package.json as well&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;scripts&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;build&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;webpack --config scripts/build.ts --node-env=prod&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;start&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;webpack serve --config scripts/start.ts --node-env=local&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;That concludes this part. You can review all the details in following PR: &lt;a href="https://github.com/Verthon/webpack-react-boilerplate/pull/2"&gt;https://github.com/Verthon/webpack-react-boilerplate/pull/2&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Currently we have setup for modern JS, dev-server and React. In upcoming next part configuration will be extended with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;providing assets handling configuration&lt;/li&gt;
&lt;li&gt;providing test configuration with Jest&lt;/li&gt;
&lt;li&gt;basic configuration for CSS&lt;/li&gt;
&lt;li&gt;adding bundle analyzer to track bundle-size in each build&lt;/li&gt;
&lt;li&gt;minimal docs in the readme&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Thank you, any feedback is highly appreciated.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>webpack</category>
      <category>typescript</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Part 1: Setting Up the Basics</title>
      <dc:creator>Krzysztof Sordyl</dc:creator>
      <pubDate>Sun, 31 Dec 2023 09:33:10 +0000</pubDate>
      <link>https://dev.to/verthon/building-a-modern-react-spa-boilerplate-with-webpack-5-part-1-setting-up-the-basics-43i3</link>
      <guid>https://dev.to/verthon/building-a-modern-react-spa-boilerplate-with-webpack-5-part-1-setting-up-the-basics-43i3</guid>
      <description>&lt;p&gt;Building a Modern React SPA Boilerplate with Webpack 5, Part 1: Setting Up the Basics&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Webpack
&lt;/h2&gt;

&lt;p&gt;Webpack is static module bundler - it basically takes your modules (needed to run your app) and turn them into one or more bundles, which are simply static assets.&lt;/p&gt;

&lt;p&gt;Bundler starts with scanning your modules, in order to create something called &lt;code&gt;dependency-graph&lt;/code&gt; . This way Webpack will know, which modules and libraries are needed for the entry-point for your application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Core concepts
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Entry - module, where Webpack starts building dependency-graph, in React apps it is often index.ts/js file which imports bootstrap file&lt;/li&gt;
&lt;li&gt;Output - location where Webpack should place the final bundle&lt;/li&gt;
&lt;li&gt;Loaders - allow Webpack to process files with extensions other than JavaScript and JSON (JSX transformations, CSS, Fonts loaders, Babel transformations)&lt;/li&gt;
&lt;li&gt;Plugins - plugins will handle the additional performance optimizations, environment variables, Module Federation etc.&lt;/li&gt;
&lt;li&gt;Mode - tells Webpack, if you are in dev mode or production, where bundler apply all of its environment based optimizations&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;You might be wondering why to use Webpack nowadays, especially with many new bundlers emerging ? There are multiple advantages to still use Webpack:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Webpack huge ecosystem of loaders and plugins, battle-proven with big community&lt;/li&gt;
&lt;li&gt;Highly customizable with great documentation&lt;/li&gt;
&lt;li&gt;Framework agnostic - in this guide we use React - but bundler is completely separated from the any framework&lt;/li&gt;
&lt;li&gt;Advanced code-splitting and lazy loading with Webpack Magic comments (&lt;a href="https://webpack.js.org/api/module-methods/#magic-comments"&gt;https://webpack.js.org/api/module-methods/#magic-comments&lt;/a&gt;) fetch priorities (&lt;a href="https://webpack.js.org/api/module-methods/#webpackfetchpriority"&gt;https://webpack.js.org/api/module-methods/#webpackfetchpriority&lt;/a&gt;) and many more advanced features&lt;/li&gt;
&lt;li&gt;Module Federation - this topic is very advanced and dedicated rather for big projects - to learn more please refer to this fantastic article: &lt;a href="https://scriptedalchemy.medium.com/understanding-webpack-module-federation-a-deep-dive-efe5c55bf366"&gt;https://scriptedalchemy.medium.com/understanding-webpack-module-federation-a-deep-dive-efe5c55bf366&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Setting up npm
&lt;/h3&gt;

&lt;p&gt;Now we need put the concepts into minimal webpack setup. We assume that repository with initialized npm setup is there. The goal for this section is to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ensure devs working on this project will have Node v20 LTS&lt;/li&gt;
&lt;li&gt;install needed npm packages&lt;/li&gt;
&lt;li&gt;add basic TypeScript setup&lt;/li&gt;
&lt;li&gt;create Webpack setup function&lt;/li&gt;
&lt;li&gt;create build script&lt;/li&gt;
&lt;li&gt;prepare environment variables config&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Unified Node version
&lt;/h3&gt;

&lt;p&gt;We always want to use Node 20 in this repository. To enforce this, create a new file named &lt;code&gt;.nvmrc&lt;/code&gt;Run the following command to enforce the use of exact package versions within the project.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"v20"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; .nvmrc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Whenever someone using NVM navigate to this project, it will automatically switch to Node 20.&lt;/p&gt;

&lt;p&gt;Sometimes it may not work automatically, so it is a good practice to always use &lt;code&gt;nvm use&lt;/code&gt; before you run npm install command.&lt;/p&gt;

&lt;h3&gt;
  
  
  Additional setup for npm
&lt;/h3&gt;

&lt;p&gt;Run this command to enforce exact versions of packages that will be installed inside of the project.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo &lt;/span&gt;engine-strict &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; .npmrc
&lt;span class="nb"&gt;echo &lt;/span&gt;save-prefix&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; .npmrc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Additionally it is good practice to add engines to package.json to make our lives easier on CICD and deployments&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="s2"&gt;"engines"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="s2"&gt;"node"&lt;/span&gt;: &lt;span class="s2"&gt;"20"&lt;/span&gt;
 &lt;span class="o"&gt;}&lt;/span&gt;,
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;To use Webpack, it is necessary to install it as a dependency in our project, along with some helper packages needed for TypeScript.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i &lt;span class="nt"&gt;-D&lt;/span&gt; webpack webpack-cli typescript ts-node @types/node @types/webpack
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Base tsconfig.json
&lt;/h3&gt;

&lt;p&gt;I strongly recommend fantastic TypeScript resources available on: &lt;a href="https://www.totaltypescript.com/"&gt;https://www.totaltypescript.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For this boilerplate, the bundler setup will do the job. Create a &lt;code&gt;tsconfig.json&lt;/code&gt; file with the following content (Reference: &lt;a href="https://www.totaltypescript.com/tsconfig-cheat-sheet"&gt;TypeScript Configurations Cheat Sheet&lt;/a&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"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="err"&gt;/*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Base&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Options:&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;"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;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;"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;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;"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;"es2022"&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;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;"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;"moduleDetection"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"force"&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="err"&gt;/*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Strictness&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;"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;"noUncheckedIndexedAccess"&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;"outDir"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dist"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"sourceMap"&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;"composite"&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;"declarationMap"&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="err"&gt;/*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;If&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;NOT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;transpiling&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;with&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;TypeScript:&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;"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;"Bundler"&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;"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="err"&gt;/*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;If&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;your&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;code&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;runs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;DOM:&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;"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;"es2022"&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"&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="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;h3&gt;
  
  
  Add minimal Webpack build configuration
&lt;/h3&gt;

&lt;p&gt;Next, create a configuration file for Webpack - &lt;code&gt;webpack.config.ts&lt;/code&gt; - in the root of the project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;path&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="kd"&gt;type&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Configuration&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;webpack&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;createWebpackConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Configuration&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;mode&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]):&lt;/span&gt; &lt;span class="nx"&gt;Configuration&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;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cwd&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;src/index.js&lt;/span&gt;&lt;span class="dl"&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;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="p"&gt;},&lt;/span&gt;
        &lt;span class="na"&gt;output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cwd&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dist&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="na"&gt;clean&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="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;createWebpackConfig&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We take advantage of having function as configuration over object to be able to customize our scripts later on.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;mode for production will be set always to &lt;code&gt;production&lt;/code&gt; - this allow Webpack to make its optimizations for production build&lt;/li&gt;
&lt;li&gt;entry points to entry file index.js&lt;/li&gt;
&lt;li&gt;output is set to the &lt;code&gt;dist&lt;/code&gt; folder, we also pass &lt;code&gt;clean&lt;/code&gt; property, to remove old dist folder whenever you are creating new build&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Example entry file and build command
&lt;/h3&gt;

&lt;p&gt;For now, we can stick with a basic &lt;code&gt;index.js&lt;/code&gt; file to simply test the build command.&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="c1"&gt;//src/index.js&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;printHelloWorld&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;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hello world&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;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;printHelloWorld&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Last but not least the build command and dedicated script:&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="c1"&gt;// scripts/build.ts&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;createWebpackConfig&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../webpack.config&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default &lt;/span&gt;&lt;span class="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;return&lt;/span&gt; &lt;span class="nf"&gt;createWebpackConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;production&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Build command in package.json&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;scripts&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;build&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;webpack --config scripts/build.ts&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;Now by running &lt;code&gt;npm run build&lt;/code&gt; new folder &lt;code&gt;dist&lt;/code&gt; should be created with following file in 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="c1"&gt;//dist/main.&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;use strict&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hello world&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;That concludes this part. You can review all the details in following PR: &lt;a href="https://github.com/Verthon/webpack-react-boilerplate/pull/1/files"&gt;https://github.com/Verthon/webpack-react-boilerplate/pull/1/files&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We now have a minimal setup, which we will extend with useful React-based features in the next article. In upcoming part we will focus on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;setting up modern Babel configuration&lt;/li&gt;
&lt;li&gt;adding React&lt;/li&gt;
&lt;li&gt;creating dev-server configuration&lt;/li&gt;
&lt;li&gt;extending Webpack plugins&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Thank you, any feedback is highly appreciated.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>tutorial</category>
      <category>webpack</category>
      <category>typescript</category>
    </item>
    <item>
      <title>Code Coverage - more than just a number</title>
      <dc:creator>Krzysztof Sordyl</dc:creator>
      <pubDate>Sat, 04 Mar 2023 15:00:57 +0000</pubDate>
      <link>https://dev.to/verthon/code-coverage-more-than-just-a-number-2f2n</link>
      <guid>https://dev.to/verthon/code-coverage-more-than-just-a-number-2f2n</guid>
      <description>&lt;h2&gt;
  
  
  Code coverage
&lt;/h2&gt;

&lt;p&gt;Code coverage is a set of tools, that examine the percentages of code covered by the tests. &lt;br&gt;
I’m working with Jest on daily basis, in this article I will use examples from this test framework.&lt;/p&gt;

&lt;p&gt;To run coverage report, you can use following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  jest &lt;span class="nt"&gt;--coverage&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can see the results right away in your terminal or open nicer GUI with coverage in &lt;code&gt;src/coverage/lcov-report/index.html&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The metrics&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;lines - simply how much of you code is taking part in the tests&lt;/li&gt;
&lt;li&gt;statements - same as above but it counts only instructions&lt;/li&gt;
&lt;li&gt;functions - how many functions were called during test&lt;/li&gt;
&lt;li&gt;branches - all branches of if/else, switches, conditional flows&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Lets look at silly example:&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="c1"&gt;// Test case&lt;/span&gt;

&lt;span class="nx"&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;isErrorRoute - helper to determine if url from input is recognized as error route in routing&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;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&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;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isErrorRoute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://example.com/error&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;toEqual&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="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isErrorRoute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://example.com/error/not-found&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Implementation&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;isErrorRoute&lt;/span&gt; &lt;span class="o"&gt;=&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="kr"&gt;string&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="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// 1. branch not covered&lt;/span&gt;
    &lt;span class="c1"&gt;// 2. both 'statements' in line 4 and 5 also not covered&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;statement&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&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;includes&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the test with following metrics&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;lines - 60% (3/5)&lt;/li&gt;
&lt;li&gt;statements - 66.66% (4/6)&lt;/li&gt;
&lt;li&gt;functions - 100% (1/1)&lt;/li&gt;
&lt;li&gt;branches - 0% (0/1)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  What information code coverage gives us ?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;what parts of application are not covered with tests at all&lt;/li&gt;
&lt;li&gt;acts as a general guide for further analysis, where there might a be a real need of adding tests&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  What information code coverage won’t give you ?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;how much behaviour is covered&lt;/li&gt;
&lt;li&gt;it tell you nothing about overall tests quality&lt;/li&gt;
&lt;li&gt;covering code with tests ≠= covering functionality with tests&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The code coverage requirements
&lt;/h3&gt;

&lt;p&gt;Most of the times there is requirement of having 80% code coverage in order to add new functionality to the codebase. &lt;/p&gt;

&lt;p&gt;While I can understand the purpose of it, there is a huge chance, that some developers may focus solely on meeting the 80% threshold, without considering the real purpose of testing.&lt;/p&gt;

&lt;p&gt;Common gotchas to spot on for “80% threshold focused” devs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Over-reliance on mocks and stubs&lt;/li&gt;
&lt;li&gt;No assertions in the tests&lt;/li&gt;
&lt;li&gt;Writing many very similar tests&lt;/li&gt;
&lt;li&gt;Ignoring code paths that are difficult to test (which may be a crucial parts for functionality)&lt;/li&gt;
&lt;li&gt;Testing implementation details rather than behavior&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>testing</category>
      <category>typescript</category>
    </item>
    <item>
      <title>Headless UI Libraries: The Key to Flexible and Accessible User Interfaces</title>
      <dc:creator>Krzysztof Sordyl</dc:creator>
      <pubDate>Sun, 18 Dec 2022 13:33:41 +0000</pubDate>
      <link>https://dev.to/verthon/headless-ui-libraries-the-key-to-flexible-and-accessible-user-interfaces-546p</link>
      <guid>https://dev.to/verthon/headless-ui-libraries-the-key-to-flexible-and-accessible-user-interfaces-546p</guid>
      <description>&lt;h2&gt;
  
  
  What are headless components ?
&lt;/h2&gt;

&lt;p&gt;In general the building UI is a very custom experience based on the brand you are building. Every company has different needs. So how to achieve that ?&lt;/p&gt;

&lt;p&gt;There are a lot of already advanced, already styled, production ready libraries out there like MUI, Mantine, Chakra, Antd etc. These libraries allow you to deliver features extremely quickly at the beginning of the project and have large and supporting community. &lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;headless components&lt;/strong&gt; gives you the small building blocks for creating UI, it comes with the freedom of styling, while maintaining the accessibility, functionality and all cross browser quirks for you.&lt;/p&gt;

&lt;p&gt;Headless components are the unstyled versions of the regular components like Select or Dropdown, that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;are fully tested on different browsers and platforms&lt;/li&gt;
&lt;li&gt;supports keyboard navigation&lt;/li&gt;
&lt;li&gt;supports the entire focus management for you&lt;/li&gt;
&lt;li&gt;supports screen readers and are fully accessible&lt;/li&gt;
&lt;li&gt;supports all that edge cases that you don't want to deal with&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example of headless component from radix-ui docs&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="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;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="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;ToggleGroup&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;@radix-ui/react-toggle-group&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;TextAlignLeftIcon&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;TextAlignCenterIcon&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;TextAlignRightIcon&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;@radix-ui/react-icons&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./styles.css&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;ToggleGroupDemo&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;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;ToggleGroup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Root&lt;/span&gt;
    &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ToggleGroup&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;single&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="nx"&gt;defaultValue&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;center&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="nx"&gt;aria&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Text alignment&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;ToggleGroup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Item&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ToggleGroupItem&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;left&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;aria&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Left aligned&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;TextAlignLeftIcon&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;/ToggleGroup.Item&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;ToggleGroup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Item&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ToggleGroupItem&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;center&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;aria&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Center aligned&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;TextAlignCenterIcon&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;/ToggleGroup.Item&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;ToggleGroup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Item&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ToggleGroupItem&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;right&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;aria&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Right aligned&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;TextAlignRightIcon&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;/ToggleGroup.Item&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;/ToggleGroup.Root&lt;/span&gt;&lt;span class="err"&gt;&amp;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;ToggleGroupDemo&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now imagine how much time you will have to spent in order to support all of those features 😅 If you are still thinking “I can surely can do that on my own” - please take a look at Pedro Duarte talk &lt;a href="https://www.youtube.com/watch?v=pcMYcjtWwVI"&gt;So You Think You Can Build A Dropdown? (Next.js Conf 2021)&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Example of headless UI libraries:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.radix-ui.com/"&gt;Radix-UI&lt;/a&gt; - low-level UI component library with a focus on accessibility, customization and developer experience that can be adopted incrementally since every component is a diffrent npm package&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://headlessui.com/"&gt;Headless UI&lt;/a&gt; - works great with TailwindCSS but has 10 components, however it supports both Vue and React&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://reakit.io/docs/get-started/"&gt;Reakit&lt;/a&gt; - comes with hooks and components built with composition in mind&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://reach.tech/"&gt;Reach UI&lt;/a&gt; - created by React Router team&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://react-spectrum.adobe.com/react-aria/index.html"&gt;React Aria&lt;/a&gt; - created by Adobe - library of hooks that provides primitives for your custom styling&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Why to even think about it ?
&lt;/h3&gt;

&lt;p&gt;As mentioned briefly above the headless components comes with a lot features, which make your life easier while building custom UI. &lt;/p&gt;

&lt;p&gt;The main reasons, that you should consider going headless for your project:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;unstyled&lt;/strong&gt; - gives you freedom how you would like to style your components (css in js, tailwind, css-modules etc.) Additionally you are not being force to follow specific library design scheme&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;bundle size&lt;/strong&gt; - is not bloated with libraries code (styling, hooks, helpers, theming etc.), they are doing great job with tree shaking, but still it is bigger than headless&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;consistency&lt;/strong&gt; - similar like in the regular UI libs the api of components is consistent, so your team mates won’t spot the difference in daily basis work and maintenance&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;incremental adoption&lt;/strong&gt; - libraries like &lt;strong&gt;radix-ui&lt;/strong&gt; serves components as standalone package which help greatly with migrating existing library step by step&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;typescript support&lt;/strong&gt; - most of libraries that will mention in this article are written in TypeScript it means that DX will be great, and package will be most likely self documenting&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;can improve the UI layer&lt;/strong&gt; - you can nicely decouple the ui from logic in your components&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;redesigns won’t be scary&lt;/strong&gt; - since you have full control of theming and styling, it is more future-proof and easier to maintain&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  When to choose a headless option ?
&lt;/h3&gt;

&lt;p&gt;The headless approach works best when you are building the components library for your organization or for your client. &lt;/p&gt;

&lt;p&gt;Additionally it will be perfect case when you are build a project which has own design system, that is different than systems that popular UI libraries are using.&lt;/p&gt;

&lt;p&gt;Given that I think most of the project should at least consider using mentioned headless libraries. &lt;/p&gt;

&lt;p&gt;Personally in the projects that I’m working on we initially choose the mui - but right now, since we have custom design system, we are gradually removing the mui and adding the radix-ui components.&lt;/p&gt;

&lt;h3&gt;
  
  
  Are “traditional” UI Library obsolete then ?
&lt;/h3&gt;

&lt;p&gt;Definitely not, they are still maintained and they are fantastic tools for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Proof of Concept like projects, when time to market is crucial&lt;/li&gt;
&lt;li&gt;Very unlikely to happen - but your design system is based on the same, that UI library uses&lt;/li&gt;
&lt;li&gt;Projects with tight deadlines or very low budget&lt;/li&gt;
&lt;li&gt;Personal projects&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Drawbacks of headless UI
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;they often require more setup on the developer side&lt;/li&gt;
&lt;li&gt;the community is not that big as traditional UI libraries&lt;/li&gt;
&lt;li&gt;you most likely won’t find every component, that you are familiar with from mui system for example&lt;/li&gt;
&lt;li&gt;may not be as widely used - most of FE devs are familiar with mui for instance, the headless ones are going to mainstream, but they are still unfortunately not widely adopted&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Summary
&lt;/h3&gt;

&lt;p&gt;In my opinion the headless component libraries will continue to gain popularity over time as more developers seek out ways to create user interfaces that are accessible, customizable, and performant.&lt;/p&gt;

&lt;p&gt;As headless component libraries continue to evolve and improve, they may become an increasingly attractive option for developers looking to create user interfaces that meet the specific needs of their projects. It's possible that traditional component libraries will also continue to adapt and offer more customization options, such as unstyled versions of their libraries, in response to the growing popularity of headless libraries.&lt;/p&gt;

&lt;p&gt;It's worth noting that some traditional component libraries, like Material-UI (&lt;a href="https://mui.com/base/getting-started/overview/"&gt;https://mui.com/base/getting-started/overview/&lt;/a&gt;) and Mantine (&lt;a href="https://mantine.dev/changelog/5-0-0/#unstyled-components"&gt;https://mantine.dev/changelog/5-0-0/#unstyled-components&lt;/a&gt;), are also now offering unstyled versions of their libraries, which can allow for more customization and control over the design of your application. &lt;/p&gt;

&lt;p&gt;This can be a good option if you want to use a traditional library but still have some of the design flexibility provided by a headless library.&lt;/p&gt;

</description>
      <category>react</category>
      <category>headlessui</category>
      <category>webdev</category>
      <category>ui</category>
    </item>
    <item>
      <title>Frontend test smells</title>
      <dc:creator>Krzysztof Sordyl</dc:creator>
      <pubDate>Sun, 30 Oct 2022 09:10:31 +0000</pubDate>
      <link>https://dev.to/verthon/frontend-test-smells-137h</link>
      <guid>https://dev.to/verthon/frontend-test-smells-137h</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;This article aims to help you find common problems in frontend testing.&lt;/p&gt;

&lt;p&gt;Our frontend applications grows very fast, along with the addition of (more and more thoughtful 😉) features, which should be tested automatically.&lt;/p&gt;

&lt;p&gt;I strongly encourage you to critique and also review the additional materials at the end of this article, that formed the basis for this list of "test smell's"&lt;/p&gt;

&lt;p&gt;List of the most common problems in testing, that I have noticed in projects and libraries that I maintain in my regular job:&lt;/p&gt;

&lt;h2&gt;
  
  
  Test smells
&lt;/h2&gt;

&lt;p&gt; &lt;/p&gt;

&lt;h3&gt;
  
  
  Using the &lt;code&gt;fireEvent&lt;/code&gt; for user interactions
&lt;/h3&gt;

&lt;p&gt;If you using a React Testing Library in your tests I would suggest to switch for &lt;code&gt;userEvent&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;UserEvent is way better at simulating the user behaviour, &lt;code&gt;fireEvent&lt;/code&gt; just dispatches the DOM event, when the &lt;code&gt;userEvent&lt;/code&gt; simulates full interactions - similarly like it is in real browser.&lt;/p&gt;




&lt;h3&gt;
  
  
  Lack of using setup for &lt;code&gt;userEvent&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;It is recommended to invoke &lt;code&gt;setup&lt;/code&gt; method before the actual component render in test.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;UserEvent&lt;/code&gt; &lt;a href="https://testing-library.com/docs/user-event/intro#writing-tests-with-userevent"&gt;authors encourage to invoke setup always before the render&lt;/a&gt;. Personally I'm sticking with helper createWrapper function. It helps with managing all our providers in once place.&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;createTestWrapper&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; 
&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;children&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;PropsWithChildren&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;unknown&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;userEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nf"&gt;render&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;SomeProvider&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;children&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;/SomeProvider&amp;gt;&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="err"&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;
  
  
  Lack of using await for the &lt;code&gt;user events&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Simply always await any user interaction&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; 
&lt;span class="nf"&gt;createTestWrapper&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;children&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;Checkbox&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;checkbox&lt;/span&gt; &lt;span class="o"&gt;=&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;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;checkbox&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;checkbox&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="err"&gt;🛑&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;checkbox&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="err"&gt;✅&lt;/span&gt;

&lt;span class="nf"&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="nf"&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;checkbox&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;toBeChecked&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The only expection from it is &lt;code&gt;.type&lt;/code&gt; according to the docs: &lt;/p&gt;

&lt;p&gt;&lt;em&gt;"To be clear, userEvent.type always returns a promise, but you only need to await the promise it returns if you're using the delay option. Otherwise everything runs synchronously and you can ignore the promise".&lt;/em&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  Huge test cases
&lt;/h3&gt;

&lt;p&gt;On the whole it is quite logical, it is definitely better to break up into several smaller cases - than to test everything in one. &lt;/p&gt;

&lt;p&gt;There is absolutely no point in artificially limiting the number of cases.&lt;/p&gt;




&lt;h3&gt;
  
  
  Too many assertions in one test case
&lt;/h3&gt;

&lt;p&gt;It will be problematic to find what exactly failed, while you have a lot of assertions that checks totally different things.&lt;/p&gt;

&lt;p&gt;Some people like to have only 1 assertion per test. I think it might be problematic, since checking one behavior might need more than just one assertion. As long as you testing one thing - you should be fine.&lt;/p&gt;

&lt;p&gt;Here it can be helpful to use these techniques: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;given =&amp;gt; when =&amp;gt; then &lt;/li&gt;
&lt;li&gt;prepare =&amp;gt; act =&amp;gt; assert&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Lack of any assertions or assertion which are useless
&lt;/h3&gt;

&lt;p&gt;Think of it as something like &lt;code&gt;expect(true).toBe(true)&lt;/code&gt; or &lt;code&gt;expect(MightyComponent).toBeDefined()&lt;/code&gt; 😉&lt;/p&gt;




&lt;h3&gt;
  
  
  Lack of strongly typed response mocks
&lt;/h3&gt;

&lt;p&gt;In case you are using &lt;a href="https://mswjs.io/"&gt;msw&lt;/a&gt;. &lt;br&gt;
I think it is best to aim to be 100% up to date in terms of api responses structure. &lt;br&gt;
By typing it all, the TypeScript compiler makes sure that these mocks are always up to date as to type.&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;// fixture for msw handlers&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;regionsDictionary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;RegionsDictionaryResponse&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;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;name_en&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Bavaria&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_ar&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="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;id&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="na"&gt;name_en&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Saxony&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_ar&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="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;name_en&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Hesse&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_ar&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="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Badly described test cases
&lt;/h3&gt;

&lt;p&gt;I see it always in codebases: &lt;code&gt;should render a component&lt;/code&gt; or &lt;code&gt;method should return true&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;I found that, it is best to show our descriptions to someone else and give that person very short time to read it and then ask what this test is actually checking ? &lt;br&gt;
If it is not obvious, then most likely our descriptions are misleading.&lt;/p&gt;

&lt;p&gt;It is crucial to think, up front - what exactly I need to test. What user interaction should I check, the more our description is for humans not 🤖 the better test is.&lt;/p&gt;


&lt;h3&gt;
  
  
  Tests that sometimes passes, sometimes not
&lt;/h3&gt;

&lt;p&gt;If it happens to your unit tests, then most likely it will be caused by some side effects. I often see the problems with time / dates.&lt;br&gt;
If there is not time for such a debugging I would suggest to just delete those problematic tests&lt;/p&gt;


&lt;h3&gt;
  
  
  Logic in your tests
&lt;/h3&gt;

&lt;p&gt;Even "simple" if statements. It is hard to determine if your logic in test failed or something is wrong with the thing that you are testing.&lt;/p&gt;

&lt;p&gt;When someone claims, that if/else logic in test can be removed for some reason, I would try to check if it can be split into two or more test cases.&lt;/p&gt;


&lt;h3&gt;
  
  
  It is hard to write any tests
&lt;/h3&gt;

&lt;p&gt;This usually indicates a complicated code, functions that do everything ? React Context that oversees the entire state ? &lt;/p&gt;


&lt;h3&gt;
  
  
  tests duplication
&lt;/h3&gt;

&lt;p&gt;Sometimes it is enough to write only integration test to check user interaction and not necessarily additionally unit-test a component / function that has already been tested in the integration test&lt;/p&gt;


&lt;h3&gt;
  
  
  very high coverage (90% +)
&lt;/h3&gt;

&lt;p&gt;Most often points to implementation testing, or worse, jacking up coverage at all costs, and no longer necessarily focusing on quality and behavioral coverage.&lt;/p&gt;


&lt;h3&gt;
  
  
  Very slow tests
&lt;/h3&gt;

&lt;p&gt;Especially the typical unit ones, we want developers to get in the habit of running tests often. &lt;br&gt;
If they are slow, no one will let them go - happily there is always CICD at the end, but it is rather poor consolation when we want to popularize testing.&lt;/p&gt;


&lt;h3&gt;
  
  
  mocking everything
&lt;/h3&gt;

&lt;p&gt;Such a test does not work much, in addition, having in our projects setup msw - mocking axios is in my opinion an average idea - when we have fantastic tools like &lt;a href="https://mswjs.io/"&gt;msw&lt;/a&gt; to deal with your api calls easily&lt;/p&gt;


&lt;h3&gt;
  
  
  tests initially are always pass ✅
&lt;/h3&gt;

&lt;p&gt;It is quite important to check whether for bad data or for reversal of the studied behavior - the test will correctly fail. &lt;br&gt;
Then we are sure that we do not fall into the so-called false positive. &lt;br&gt;
Here I refer to the article by &lt;a href="https://khorikov.org/posts/2020-01-29-false-positives-negatives/"&gt;Vlad Khorikov&lt;/a&gt;&lt;/p&gt;


&lt;h3&gt;
  
  
  Tests don't act as living documentation
&lt;/h3&gt;

&lt;p&gt;Good tests should be easy for the person reading them.&lt;br&gt;
You can always check it by folding all test cases in your IDE and reading its description only, if it gives you a brief idea what behavior is being tested then everything is fine.&lt;br&gt;
Below I cite some of the most important features developed by &lt;strong&gt;Gerard Meszaros&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The guiding principle, of course, is to write tests for people not robots, a good test:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;describes the context, starting point or preconditions that must be met

illustrates how the code is called

variables or names in the test are trivial for the recipient to understand

describes exactly the expected result or the secondary conditions it verifies

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

&lt;/div&gt;






&lt;h3&gt;
  
  
  Lack of any test
&lt;/h3&gt;

&lt;p&gt;The biggest red flag that I can think of 🛑 . It is best to start by testing the simplest utils or even (it might be controversial) write empty test cases with detailed business descriptions of the functionality. &lt;br&gt;
When the time is right, someone can help with writing such tests, or you can learn it yourself. &lt;/p&gt;

&lt;h2&gt;
  
  
  Material that helped me a lot preparing this article:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt; &lt;a href="https://kentcdodds.com/blog/common-mistakes-with-react-testing-library"&gt;https://kentcdodds.com/blog/common-mistakes-with-react-testing-library&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kentcdodds.com/blog/the-testing-trophy-and-testing-classifications"&gt;https://kentcdodds.com/blog/the-testing-trophy-and-testing-classifications&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://khorikov.org/posts/2020-01-29-false-positives-negatives/"&gt;https://khorikov.org/posts/2020-01-29-false-positives-negatives/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://xunitpatterns.com/Test%20Smells.html"&gt;http://xunitpatterns.com/Test%20Smells.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=wCx_6kOo99M&amp;amp;t=1808s"&gt;https://www.youtube.com/watch?v=wCx_6kOo99M&amp;amp;t=1808s&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>testing</category>
      <category>react</category>
      <category>frontend</category>
      <category>typescript</category>
    </item>
  </channel>
</rss>
