<?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: Antoine</title>
    <description>The latest articles on DEV Community by Antoine (@towaanu).</description>
    <link>https://dev.to/towaanu</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%2F210724%2F222f6a64-0878-433e-96f3-547550f7a134.png</url>
      <title>DEV Community: Antoine</title>
      <link>https://dev.to/towaanu</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/towaanu"/>
    <language>en</language>
    <item>
      <title>Let's make a stopwatch pwa !</title>
      <dc:creator>Antoine</dc:creator>
      <pubDate>Thu, 16 Sep 2021 13:42:05 +0000</pubDate>
      <link>https://dev.to/towaanu/let-s-make-a-stopwatch-pwa-2dje</link>
      <guid>https://dev.to/towaanu/let-s-make-a-stopwatch-pwa-2dje</guid>
      <description>&lt;p&gt;In this tutorial, we are going to transform a basic webapp into a Progressive Web App (PWA).&lt;br&gt;
The webapp we are going to use is a stopwatch. I won't explain the stopwatch implementation in this tutorial.&lt;br&gt;
The stopwatch doesn't use external libraries or framework in order to focus on the PWA aspect.&lt;br&gt;
At the end of the tutorial, the stopwatch app will be installable on an android smartphone !&lt;/p&gt;

&lt;p&gt;The source code of the project is available here: &lt;a href="https://github.com/towaanu/stopwatch-pwa" rel="noopener noreferrer"&gt;https://github.com/towaanu/stopwatch-pwa&lt;/a&gt;.&lt;br&gt;
The final app is available here: &lt;a href="https://stopwatch.towaanu.com" rel="noopener noreferrer"&gt;stopwatch.towaanu.com&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For the rest of the article, I will refer to &lt;strong&gt;Progressive Web App as PWA&lt;/strong&gt;.&lt;/p&gt;


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

&lt;p&gt;Progressive Web Apps are webapps using a set of features in order to look like a native app. A PWA tries to reduce as much as it can the barrier between webapp and native app.&lt;/p&gt;

&lt;p&gt;Here are some features used by PWA:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Reduce as much as possible loading/starting time&lt;/strong&gt; of the app using caches.&lt;/li&gt;
&lt;li&gt;A PWA can work even without network. It can be started in &lt;strong&gt;offline mode&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;A PWA can be &lt;strong&gt;installed natively&lt;/strong&gt;. For example, you can install a PWA on your mobile and open the app from your mobile homescreen, like you would for any other native app.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can find feedbacks from projects using PWA on this site: &lt;a href="https://www.pwastats.com/" rel="noopener noreferrer"&gt;https://www.pwastats.com/&lt;/a&gt;.&lt;br&gt;
Of course, there is a little work to transform a webapp into a PWA !&lt;/p&gt;

&lt;p&gt;Let's see, how we can transform our little stopwatch webapp into a PWA and install it on a mobile !&lt;/p&gt;


&lt;h2&gt;
  
  
  Stopwatch webapp
&lt;/h2&gt;

&lt;p&gt;The Stopwatch webapp is a simple app. We can start, stop or reset a timer.&lt;br&gt;
Here is an example of the app:&lt;/p&gt;

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

&lt;p&gt;You can find the final version of the stopwatch here: &lt;a href="https://stopwatch.towaanu.com" rel="noopener noreferrer"&gt;stopwatch.towaanu.com&lt;/a&gt;.&lt;br&gt;
It does not use any external framework or library.&lt;/p&gt;
&lt;h2&gt;
  
  
  The project
&lt;/h2&gt;

&lt;p&gt;There are 3 files in the starting project : &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/towaanu/stopwatch-pwa/blob/12addb23ab334b82c81bfd91c6b437cf5f013fdb/index.html" rel="noopener noreferrer"&gt;index.html&lt;/a&gt;: The html of the stopwatch&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/towaanu/stopwatch-pwa/blob/12addb23ab334b82c81bfd91c6b437cf5f013fdb/main.js" rel="noopener noreferrer"&gt;main.js&lt;/a&gt;: The main javascript file handling click events and stopwatch&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/towaanu/stopwatch-pwa/blob/12addb23ab334b82c81bfd91c6b437cf5f013fdb/style.css" rel="noopener noreferrer"&gt;style.css&lt;/a&gt;: The css of the app&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can find the starting project on this repo: &lt;a href="https://github.com/towaanu/stopwatch-pwa/tree/12addb23ab334b82c81bfd91c6b437cf5f013fdb" rel="noopener noreferrer"&gt;https://github.com/towaanu/stopwatch-pwa/tree/12addb23ab334b82c81bfd91c6b437cf5f013fdb&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Since I will focus on the PWA part in this tutorial, I won't explain in details the implementation of the stopwatch.&lt;/p&gt;
&lt;h2&gt;
  
  
  Start the app
&lt;/h2&gt;

&lt;p&gt;When working with a PWA, it's better to serve the app using a server rather than opening directly files from your machine.&lt;br&gt;
I will introduce how to serve files using &lt;strong&gt;docker&lt;/strong&gt;, &lt;strong&gt;nodejs&lt;/strong&gt;, &lt;strong&gt;python&lt;/strong&gt; however you can use other techniques to serve the project locally.&lt;br&gt;
Usually, the app should be served over localhost.&lt;/p&gt;
&lt;h3&gt;
  
  
  Docker
&lt;/h3&gt;

&lt;p&gt;If you have docker installed, you can use &lt;a href="https://hub.docker.com/_/nginx" rel="noopener noreferrer"&gt;the nginx image&lt;/a&gt; to serve any files using a http server.&lt;br&gt;
You need to be at the root of the project, then you can do:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;-p&lt;/span&gt; 8080:80 &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="nb"&gt;pwd&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;:/usr/share/nginx/html:ro nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then the stopwatch webapp &lt;em&gt;(index.html, main.js, style.css)&lt;/em&gt; should be accessible at &lt;a href="http://localhost:8080" rel="noopener noreferrer"&gt;http://localhost:8080&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Nodejs
&lt;/h3&gt;

&lt;p&gt;If you have nodejs installed locally, you can use &lt;a href="https://github.com/http-party/http-server" rel="noopener noreferrer"&gt;http-server&lt;/a&gt; to start the http server.&lt;br&gt;
You need to be at the root of the project, then you can do:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx http-server &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then the stopwatch webapp &lt;em&gt;(index.html, main.js, style.css)&lt;/em&gt; should be accessible at &lt;a href="http://localhost:8080" rel="noopener noreferrer"&gt;http://localhost:8080&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Python
&lt;/h3&gt;

&lt;p&gt;If you have python installed locally, you can use the following command at the root of the project :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 &lt;span class="nt"&gt;-m&lt;/span&gt; http.server 8080
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then the stopwatch webapp &lt;em&gt;(index.html, main.js, style.css)&lt;/em&gt; should be accessible at &lt;a href="http://localhost:8080" rel="noopener noreferrer"&gt;http://localhost:8080&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Great, the app is accessible at &lt;a href="http://localhost:8080" rel="noopener noreferrer"&gt;http://localhost:8080&lt;/a&gt; !&lt;br&gt;
Now let's transform the stopwatch webapp into a PWA ! &lt;/p&gt;


&lt;h1&gt;
  
  
  Webapp =&amp;gt; PWA
&lt;/h1&gt;

&lt;p&gt;For the rest of the tutorial we are going to use the chrome dev tool and more specifically &lt;a href="https://github.com/GoogleChrome/lighthouse" rel="noopener noreferrer"&gt;the lighthouse tool&lt;/a&gt;.&lt;br&gt;
Lighthouse can provide some feedbacks about what we need to turn a webapp into a PWA.&lt;/p&gt;
&lt;h2&gt;
  
  
  Where to start ?
&lt;/h2&gt;

&lt;p&gt;Let's see what lighthouse tells us about the app:&lt;/p&gt;

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

&lt;p&gt;Wow ! There is a lots of things to do. This is normal, we didn't have done anything to add PWA features to the app.&lt;br&gt;
At first, we are going to focus on the &lt;strong&gt;Installable&lt;/strong&gt; part.&lt;/p&gt;
&lt;h3&gt;
  
  
  Installable
&lt;/h3&gt;

&lt;p&gt;Installable means the PWA can be installed on a device like any other native app.&lt;br&gt;
For example you can install it on a smartphone and launch it like any other app !&lt;br&gt;
Lighthouse tells us : &lt;code&gt;Web app manifest or service worker do not meet the installability requirements&lt;/code&gt;.&lt;br&gt;
What is a &lt;strong&gt;Web app manifest&lt;/strong&gt; and a &lt;strong&gt;service worker&lt;/strong&gt; ? Let's see it now !&lt;/p&gt;


&lt;h3&gt;
  
  
  Web app manifest
&lt;/h3&gt;

&lt;p&gt;The web app manifest is a &lt;strong&gt;json file&lt;/strong&gt;, commonly named &lt;code&gt;manifest.json&lt;/code&gt;. This file contains data to help device display additional information when the PWA is installed.&lt;br&gt;
You can define a lots of information such as name, short name, description, colors etc...&lt;br&gt;
All properties are not mandatory for an app to be installable.&lt;br&gt;
Let's make a &lt;a href="https://github.com/towaanu/stopwatch-pwa/blob/main/manifest.json" rel="noopener noreferrer"&gt;manifest.json&lt;/a&gt; for the stopwatch app:&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;"short_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Stopwatch"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"A stopwatch pwa"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"icons"&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;span class="nl"&gt;"src"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/images/icon-192.png"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"image/png"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"sizes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"192x192"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"purpose"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"any maskable"&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;span class="nl"&gt;"src"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/images/icon-512.png"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"image/png"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"sizes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"512x512"&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;span class="nl"&gt;"start_url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"display"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"standalone"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"scope"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"theme_color"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"#ff5500"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"background_color"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"#ff5500"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's analyze &lt;a href="https://github.com/towaanu/stopwatch-pwa/blob/main/manifest.json" rel="noopener noreferrer"&gt;manifest.json&lt;/a&gt; fields:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;name&lt;/strong&gt; or &lt;strong&gt;short_name&lt;/strong&gt;: The name of the app. This name is used by the device to display the name of the app on home screen for example. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;icons&lt;/strong&gt;: List of icons to be used when the app is installed. You can provide any numbers of icons with different sizes. However, you can only provide 2 sizes : 192x192 and 512x512 and devices should be able to scale icons if needed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;theme_color&lt;/strong&gt;: The theme color of the app. It can be used to colorize the topbar (of a smartphone for example) or the browser UI when displaying the webapp.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;background_color&lt;/strong&gt;: The background_color can be used as a splashscreen when the app is loading on mobile.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;start_url&lt;/strong&gt;: The start url of the app. We need to specify the start_url, in order to know which url to load when you open an installed PWA app. ( most of the time it's &lt;code&gt;/&lt;/code&gt; )&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;display&lt;/strong&gt;: How the app should be display. Possible values are: &lt;code&gt;fullscreen&lt;/code&gt;, &lt;code&gt;standalone&lt;/code&gt;, &lt;code&gt;minimal-ui&lt;/code&gt;, &lt;code&gt;browser&lt;/code&gt;. &lt;code&gt;standalone&lt;/code&gt; means that the app should be displayed like any other native apps.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are more properties you can use in manifest.json. You can find more information about manifest's properties on &lt;a href="https://developer.mozilla.org/en-US/docs/Web/Manifest#specifications" rel="noopener noreferrer"&gt;mdn web doc&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Nice! We have our manifest.json file, but we still need to include it in our app.&lt;br&gt;
We can add the &lt;code&gt;manifest.json&lt;/code&gt; file by adding this line in &lt;code&gt;index.html&lt;/code&gt; (inside &lt;code&gt;head&lt;/code&gt; tag):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;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;title&amp;gt;&lt;/span&gt;Stopwatch&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"apple-touch-icon"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/images/icons-192.png"&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"&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;"theme-color"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"#ff5500"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

    &lt;span class="c"&gt;&amp;lt;!-- Manifest is added here --&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"manifest"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/manifest.json"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"main.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/style.css"&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Note: manifest.json is assumed to be at the root of the project in this example.&lt;/em&gt;&lt;br&gt;
You can find the updated project with manifest here: &lt;a href="https://github.com/towaanu/stopwatch-pwa/tree/2193c3fa88d451c8842001b362e06a55d9b4041d" rel="noopener noreferrer"&gt;https://github.com/towaanu/stopwatch-pwa/tree/2193c3fa88d451c8842001b362e06a55d9b4041d&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Our web app manifest is now configured ! If you try to run a test with lighthouse again, you should see that the app is not yet installable.&lt;br&gt;&lt;br&gt;
In fact, we also need a &lt;strong&gt;service worker&lt;/strong&gt; to make the app installable. Let's see what is a service worker !&lt;/p&gt;


&lt;h3&gt;
  
  
  Service Worker
&lt;/h3&gt;

&lt;p&gt;As I said, a PWA needs to be usable offline. This way, it can act as a native app. In order to be used offline, a PWA needs to cache a lots of assets &lt;em&gt;( images, html, css, js ...)&lt;/em&gt;. This is where the service worker comes into play !&lt;/p&gt;

&lt;p&gt;Service workers enable us to control how assets should be cached. Basically a service worker is between the app and the internet. The service worker can intercept every network request from the webapp and decide whether or not it should return cache data or let the request go over the network. The service worker is also responsible to handle how elements are cached.&lt;/p&gt;

&lt;p&gt;The service worker can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Intercept every request from the webapp.&lt;/li&gt;
&lt;li&gt;Decide whether or not a request should go over the network.&lt;/li&gt;
&lt;li&gt;Return cache values when cache values are available.&lt;/li&gt;
&lt;li&gt;Precache assets when the app starts.&lt;/li&gt;
&lt;li&gt;Cache value return from network requests.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here is a schema showing how service worker works when the webapp wants to fetch an images:&lt;/p&gt;

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

&lt;p&gt;&lt;em&gt;Note: Service worker can intercept any requests, not only images.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Now that we've seen what the service worker can do, let's implement one !&lt;/p&gt;
&lt;h4&gt;
  
  
  Register the service worker
&lt;/h4&gt;

&lt;p&gt;Before creating our service worker, we need to register it.&lt;br&gt;
We are going to register it at the beginning of the &lt;code&gt;main.js&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// main.js&lt;/span&gt;

&lt;span class="c1"&gt;// Check if browsers support service worker&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;serviceWorker&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;load&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Register the service worker&lt;/span&gt;
    &lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;serviceWorker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/sw.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;registration&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// Registration was successful&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="s1"&gt;ServiceWorker registration successful &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;registration&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// registration failed&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="s1"&gt;ServiceWorker registration failed: &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// stopwatch code...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And that's it for registering the service worker. As you can see, we are trying to load a &lt;code&gt;sw.js&lt;/code&gt; file.&lt;br&gt;
Let's create the &lt;code&gt;sw.js&lt;/code&gt; file.&lt;/p&gt;
&lt;h4&gt;
  
  
  Create the service worker
&lt;/h4&gt;

&lt;p&gt;The &lt;code&gt;sw.js&lt;/code&gt; file is our service worker file.&lt;br&gt;
Let's create the &lt;code&gt;sw.js&lt;/code&gt; file at the root of the project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;CACHE_NAME&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cache-v1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;urlsToCache&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&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;/main.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/style.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="c1"&gt;// Event triggered the first time service worker is installed&lt;/span&gt;
&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;install&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="cm"&gt;/*
     * Here we are caching urls specified above
     * This way when the app needs it files will be cached
     * Even if we close the app, and open later, files will still be in cache
     */&lt;/span&gt;
  &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;waitUntil&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;caches&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;CACHE_NAME&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Cache url defined in urlsToCache&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;urlsToCache&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Event triggered when the service worker is activated&lt;/span&gt;
&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;activate&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// We don't need to do anything special here for this project&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;Service worker activated&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;// Event triggered whenever webapp needs to fetch a resource&lt;/span&gt;
&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fetch&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;respondWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="c1"&gt;// Check if the request is in the cache&lt;/span&gt;
    &lt;span class="nx"&gt;caches&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="cm"&gt;/*
         * Found the request in cache
         * We can return the response in cache
         * We don't need to process the request
         */&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;response&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;response&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="cm"&gt;/*
         * Request not found in cache
         * The request is processed and the result is returned
         */&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="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;em&gt;Note: To keep things simple, I decided not to cache requests after fetch.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;As you can see we can listen to several events related to service worker.&lt;br&gt;
Those events are called lifecycle events of a service worker. There are 3 events: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;install&lt;/strong&gt;: This event is fired only once, when the service worker is installed for the first time. We are using it to precache some assets from our stopwatch app&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;activate&lt;/strong&gt;: This event is fired when the service worker is activated. It can be useful to use this event when you update your service worker and you want to clean up cache before activating a new service worker&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;fetch&lt;/strong&gt;: This event is fired every time the app is trying to make a request. This is where we can decide whether to process or not the request. We can also return the cache if the request has already been cached&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now, when the app is launched for the first time it will cache &lt;code&gt;/&lt;/code&gt;, &lt;code&gt;/main.js&lt;/code&gt; and &lt;code&gt;/style.css&lt;/code&gt;. Then whenever one of those paths is requested, the service worker will return the value in cache without making the request to the network.&lt;br&gt;
Great ! We successfully created the service worker.&lt;/p&gt;

&lt;p&gt;You can find the updated version with &lt;a href="https://github.com/towaanu/stopwatch-pwa/blob/main/sw.js" rel="noopener noreferrer"&gt;service worker&lt;/a&gt; of the project in this repo: &lt;a href="https://github.com/towaanu/stopwatch-pwa" rel="noopener noreferrer"&gt;https://github.com/towaanu/stopwatch-pwa&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Workbox
&lt;/h4&gt;

&lt;p&gt;The service worker here is really basic. However, sometime you need a more complex service worker with special rules to cache specific files or request. There is library commonly used to deal with service worker.&lt;br&gt;
This library is &lt;a href="https://developers.google.com/web/tools/workbox" rel="noopener noreferrer"&gt;Workbox&lt;/a&gt;. With workbox you can easily configure strategies for your service worker : &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Stale-While-Revalidate&lt;/strong&gt;: Responds with the cache value if it exists, otherwise use the request result. Even if the cache value is returned, Stale While Revalidate strategies will fetch the request and update the cache for the next request.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cache-First&lt;/strong&gt;: Always return the cache value. If the request is not cached, requests will be processed and the cache will be updated.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Network-First&lt;/strong&gt;: Always return the value returned by the request. If the request failed, fallback to the cache value.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Network-Only&lt;/strong&gt;: Always return the value returned by the request. Cache is not used even if request failed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cache-Only&lt;/strong&gt;: Always return the value from the cache. Network request is not used even if value is not in cache.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In a real world application you probably want to use a library like &lt;a href="https://developers.google.com/web/tools/workbox" rel="noopener noreferrer"&gt;Workbox&lt;/a&gt; for service worker.&lt;br&gt;
You can learn more about Workbox strategies on the &lt;a href="https://developers.google.com/web/tools/workbox/modules/workbox-strategies" rel="noopener noreferrer"&gt;Workbox documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Install the app
&lt;/h3&gt;

&lt;p&gt;Now that we have our manifest and service worker, let's run lighthouse again !&lt;/p&gt;

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

&lt;p&gt;&lt;em&gt;Note: The only point left is the redirection HTTP =&amp;gt; HTTPS. This is something to be configured at the server level ( by using nginx for example ).&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Lighthouse tells us that our app meet the requirements to be installed. However if you try to access the app, you can not install it.&lt;br&gt;
For security reasons, a PWA can only be installed if it is served from an &lt;strong&gt;HTTPS&lt;/strong&gt; endpoint. &lt;br&gt;
Since we are testing the app in local, it's complicated to serve the app over https.&lt;/p&gt;

&lt;p&gt;You can try and install the app using this url: &lt;a href="https://stopwatch.towaanu.com" rel="noopener noreferrer"&gt;https://stopwatch.towaanu.com&lt;/a&gt;.&lt;br&gt;
On the above url, the app is served using https, you should be able to install it!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw5oalfa636hc9y1za3qk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw5oalfa636hc9y1za3qk.png" alt="PWA install on mobile" width="800" height="343"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Nice ! We have successfully add PWA features to a webapp. The stopwatch app can now be installed and used like any other native app !&lt;/p&gt;




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

&lt;p&gt;I hope this tutorial helps you understand how PWA works!&lt;br&gt;
You can find a working version of the project here : &lt;a href="https://stopwatch.towaanu.com/" rel="noopener noreferrer"&gt;https://stopwatch.towaanu.com/&lt;/a&gt;&lt;br&gt;
The source code is available on this repo : &lt;a href="https://github.com/towaanu/stopwatch-pwa" rel="noopener noreferrer"&gt;https://github.com/towaanu/stopwatch-pwa&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Most of popular frameworks such as react, vue, angular... provide tools to generate app with pwa features included. Usually, tools generate a service worker and a manifest.json that you can customize.&lt;br&gt;
If you want to see a PWA app using React, I have an opensource pwa project here: &lt;a href="https://memodogs.towaanu.com" rel="noopener noreferrer"&gt;memodogs.towaanu.com&lt;/a&gt;. &lt;em&gt;(You can find the source on this repo : &lt;a href="https://github.com/towaanu/memodogs" rel="noopener noreferrer"&gt;https://github.com/towaanu/memodogs&lt;/a&gt;)&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;I hope you enjoy this article :)&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>mobile</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Introduction to typescript with React</title>
      <dc:creator>Antoine</dc:creator>
      <pubDate>Thu, 16 Sep 2021 13:30:28 +0000</pubDate>
      <link>https://dev.to/towaanu/introduction-to-typescript-with-react-57m7</link>
      <guid>https://dev.to/towaanu/introduction-to-typescript-with-react-57m7</guid>
      <description>&lt;p&gt;In this article, I would like to share with you how I use typescript to improve my react code.&lt;br&gt;
First we are going to see what is typescript and why it is used ?&lt;br&gt;
Then, how we can use typescript with react ( components, hooks, extern libs ).&lt;br&gt;
Finally, I will sum up what is the pros and cons using typescript in a react app .&lt;/p&gt;
&lt;h2&gt;
  
  
  Typescript ?
&lt;/h2&gt;

&lt;p&gt;A common critic about javascript is that javascript is untyped. It means that you can do:&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;let&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Hello&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Here we substract a number to a string&lt;/span&gt;
    &lt;span class="c1"&gt;// Javascript does not warn us even if we try to substract a number to a string&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;b&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="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// Hello&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="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// 5&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="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// NaN&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see javascript is very permissive which can lead to unexpected behaviors and bugs.&lt;br&gt;
Another recurrent critic is that, we don't know fields of objects in javascript.&lt;br&gt;
Sometime we get an object and we are not sure what is the structure of this object.&lt;br&gt;
For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Eikichi&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;lastName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Onizuka&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="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;// user.name does not exist.&lt;/span&gt;
&lt;span class="c1"&gt;// Javascript is unable to tell us field name does not exist&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These 2 samples can potentially provide error at runtime. It would be great to have hints before trying the code about potential error like ones above.&lt;br&gt;
Typescript tries to address those issues by adding types to javascript.&lt;br&gt;
Typescript is a programming language. By adding types, typescript is able to give some hints before running the code.&lt;br&gt;
Typescript is not executed in the browser directly, typescript is first transformed into javascript code.&lt;br&gt;
In the end, only javascript is executed in the browser when using typescript.&lt;/p&gt;

&lt;p&gt;Now, let's see how we can use typescript alongside React ! &lt;/p&gt;
&lt;h2&gt;
  
  
  The project
&lt;/h2&gt;

&lt;p&gt;I am going to use some code sample from a basic todo list app using react and typescript.&lt;br&gt;
In the app, we can add todo and toggle todo to make them done. &lt;br&gt;
Todos will have 3 fields:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;id: a &lt;a href="https://en.wikipedia.org/wiki/Universally_unique_identifier" rel="noopener noreferrer"&gt;universally unique identifier&lt;/a&gt; (uuid)&lt;/li&gt;
&lt;li&gt;label: label of the todo&lt;/li&gt;
&lt;li&gt;isDone: a boolean, true if todo is done
Here is an example of the app:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The project is created using &lt;a href="https://create-react-app.dev/" rel="noopener noreferrer"&gt;create react app&lt;/a&gt;.&lt;br&gt;
Create react app provides a template using react and typescript to get started quickly.&lt;br&gt;
The goal of the project is to provide some react/typescript example in a little project. Styling is not important.&lt;br&gt;
You can find the code of the project &lt;a href="https://github.com/towaanu/typescript-react-todo-example" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;br&gt;
Here is a screenshot of the todos app:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyox2h7xhp9sjvbdegt7d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyox2h7xhp9sjvbdegt7d.png" alt="todos preview" width="646" height="378"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  React with Typescript
&lt;/h2&gt;

&lt;p&gt;In this part we will see how we can use typescript with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;components&lt;/li&gt;
&lt;li&gt;hooks&lt;/li&gt;
&lt;li&gt;external libs&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Shared types
&lt;/h3&gt;

&lt;p&gt;Usually, there are types you need in several parts of your application. For example, a Todo type might be used in several components.&lt;br&gt;
I define these types in a &lt;em&gt;types.ts&lt;/em&gt; file at the root of the project. This way, we can access shared types easily across the app.&lt;br&gt;
To define types, we use the &lt;code&gt;interface&lt;/code&gt; keyword in typescript. Let's analyze how it's done in the todo app!&lt;/p&gt;
&lt;h3&gt;
  
  
  Todo app
&lt;/h3&gt;

&lt;p&gt;As I said in the previous section todos have the following fields : &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;id: uuid. A &lt;a href="https://en.wikipedia.org/wiki/Universally_unique_identifier" rel="noopener noreferrer"&gt;uuid&lt;/a&gt; is a 128 bit number. &lt;/li&gt;
&lt;li&gt;label: The label corresponding to the todo. This is represented as a &lt;strong&gt;string&lt;/strong&gt; in our app.&lt;/li&gt;
&lt;li&gt;isDone: A &lt;strong&gt;boolean&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's see how we can define the Todo type to use it later in our react app.&lt;br&gt;
As I said before, all shared types are in the &lt;a href="https://github.com/towaanu" rel="noopener noreferrer"&gt;types.ts&lt;/a&gt; file.&lt;br&gt;
Here is a sample of types.ts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;Todo&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
    &lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
    &lt;span class="nx"&gt;isDone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We named this new type &lt;code&gt;Todo&lt;/code&gt;.&lt;br&gt;
Finaly we assign fields with their respective types:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;id : string, uuid will be represented as a string (example: "123e4567-e89b-12d3-a456-426614174000")&lt;/li&gt;
&lt;li&gt;label: string, the label will be represented as a string (example: "Cook")&lt;/li&gt;
&lt;li&gt;isDone: boolean (example: true)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Great ! We have our Todo interface. We can now use it in the code like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Todo&lt;/span&gt; &lt;span class="o"&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;123e4567-e89b-12d3-a456-426614174000&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Cook&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;isDone&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, we can specify the type of a variable using &lt;code&gt;:&lt;/code&gt; in typescript.&lt;br&gt;
If we try to access or to add a non present field, typescript will display an error.&lt;/p&gt;

&lt;p&gt;We also need a NewTodo type. This type will be used to add a new todo in the list.&lt;br&gt;
It is the same type as the Todo above except it does not have an id yet.&lt;br&gt;
Here is the code behind in &lt;code&gt;types.ts&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;NewTodo&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
    &lt;span class="nx"&gt;isDone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can now use our todos types inside components.&lt;br&gt;
Let's see how we can organize our components! &lt;/p&gt;
&lt;h2&gt;
  
  
  React components
&lt;/h2&gt;

&lt;p&gt;In React components, I like to define a Props interface before declaring the component. &lt;br&gt;
This Props interface contains every property of the component.&lt;/p&gt;

&lt;p&gt;In my opinion, here are the pros of writing the Props interface : &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It forces us to think about what properties our component needs&lt;/li&gt;
&lt;li&gt;If you open the file, you can quickly find out the parameters of the component ( you don't have to look at the component code to know what parameters it can take ) &lt;/li&gt;
&lt;li&gt;When we use the component in our App, typescript can warn us if we pass wrong parameters to our component. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's see a concrete example from the todo app!&lt;/p&gt;
&lt;h3&gt;
  
  
  Todo app
&lt;/h3&gt;

&lt;p&gt;We are going to analyze the TodosList component. Its role is to display a list of todos.&lt;br&gt;
It takes 2 parameters:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;todos: This is the list of todos that will be displayed.&lt;/li&gt;
&lt;li&gt;onTodoClick: A callback called when a todo is clicked. This callback takes a todo as a parameter.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's see how we can define this React component with typescript.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Todo&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;./types&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// import the Todo type&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;TodoItem&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;./TodoItem&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// TodoItem is the component used to display one todo on the screen&lt;/span&gt;

&lt;span class="cm"&gt;/*
 * We define our Props type
 * It is used to define the props our TodosList will take in parameter
 */&lt;/span&gt;
&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;Props&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;todos&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Todo&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;onTodoClick&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Todo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cm"&gt;/*
 * The TodosList component.
 * We are using our Props type to tell typescript "This component uses the Props type for its parameter".
 * This way, when we use our component, typescript is able to tell you if we try to use a non existing property. 
 * Or if we try to give a bad type to a props.
 */&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;TodosList&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="nx"&gt;todos&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;onTodoClick&lt;/span&gt;&lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="nx"&gt;Props&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="cm"&gt;/* 
     * Now we can use todos and the onTodoClick
     * if we try to write : `todos.foo`, typescript can tell us that an array of todos has no "foo" property
     * Same things apply to onTodoClick. If we try to call onTodoClick like this : onTodoClick(10)
     * Typescript is able to say "10 is not a todo, onTodoClick takes a todo as a parameter not a number"
     */&lt;/span&gt;
    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;ul&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="si"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;todos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;todo&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;TodoItem&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;onTodoClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;onTodoClick&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;)&lt;/span&gt; &lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;ul&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="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;TodosList&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Note: You can notice we have added a "?" to onTodoClick. It means that onTodoClick is optional.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Let's see what happens now if we try to use our component in another file:&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="cm"&gt;/* Typescript warns us, because hello does not exist as a parameter for our TodosList */&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;TodosList&lt;/span&gt; &lt;span class="na"&gt;hello&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;world&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt; 

&lt;span class="cm"&gt;/* Typescript warns us, because badTodos are missing id and label. */&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;badTodos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="na"&gt;isDone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;isDone&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;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;TodosList&lt;/span&gt; &lt;span class="na"&gt;todos&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;badTodos&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, typescript can help us avoid bugs before running the code.&lt;br&gt;
You can find another example of a component in the &lt;a href="https://github.com/towaanu/typescript-react-todo-example/blob/main/src/TodoItem.tsx" rel="noopener noreferrer"&gt;TodoItem.tsx&lt;/a&gt; file.&lt;/p&gt;

&lt;p&gt;Now let's see how we can use typescript with hooks!&lt;/p&gt;
&lt;h2&gt;
  
  
  Hooks
&lt;/h2&gt;

&lt;p&gt;There is several hooks. I will focus on useState for this article.&lt;br&gt;
&lt;a href="https://reactjs.org/docs/hooks-state.html" rel="noopener noreferrer"&gt;The useState hook&lt;/a&gt; enables us to keep a state in our component.&lt;br&gt;
With typescript we can define what state we want to store with useState.&lt;br&gt;
Typescript will then use this information to prevent us from setting a state with a wrong type.&lt;br&gt;
Let's see an example :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/*
 * Typescript now knows that num is a number and setNum takes a number as a parameter.
 * Typescript will warn us if we try to call setNum("a"), for example.
 */&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;num&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setNum&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's see an example in the todo App!&lt;/p&gt;

&lt;h2&gt;
  
  
  Todo app
&lt;/h2&gt;

&lt;p&gt;In the todo app, we need the &lt;code&gt;useState&lt;/code&gt; hook to manage todos.&lt;/p&gt;

&lt;p&gt;Let's see the &lt;a href="https://github.com/towaanu/typescript-react-todo-example/blob/main/src/App.tsx" rel="noopener noreferrer"&gt;App.tsx&lt;/a&gt; code :&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;styles&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;./App.module.css&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;v4&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;uuidv4&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;uuid&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;Todo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;NewTodo&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;./types&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;useState&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;TodosList&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;./TodosList&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;AddTodo&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;./AddTodo&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;App&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="cm"&gt;/*
   * With useState&amp;lt;Todo[]&amp;gt;, typescript knows: 
   * - todos is an Array of todos 
   * - setTodos takes an array of todos as parameter
   */&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;todos&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setTodos&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Todo&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;uuidv4&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Cleaning&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;isDone&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;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;uuidv4&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Cooking&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;isDone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;])&lt;/span&gt;

  &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;toggleTodo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Todo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;setTodos&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;todos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="nx"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;{...&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;isDone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isDone&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;t&lt;/span&gt;
      &lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;addTodo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newTodo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;NewTodo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="cm"&gt;/*
     * If we try to pass a non todos array, typescript will tell us
     */&lt;/span&gt;
    &lt;span class="nf"&gt;setTodos&lt;/span&gt;&lt;span class="p"&gt;([...&lt;/span&gt;&lt;span class="nx"&gt;todos&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;newTodo&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="nf"&gt;uuidv4&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;}])&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;App&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* Since useState is typed, typescript knows that we are passing a todos array in TodosList */&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;TodosList&lt;/span&gt; &lt;span class="na"&gt;onTodoClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;toggleTodo&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;todos&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;todos&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;AddTodo&lt;/span&gt; &lt;span class="na"&gt;onNewTodoSubmit&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;addTodo&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&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="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since useState is typed, typescript makes sure, we are not using todos and setTodos wrongfully. &lt;/p&gt;

&lt;p&gt;Notice that we use an external library ( &lt;a href="https://github.com/uuidjs/uuid" rel="noopener noreferrer"&gt;uuid&lt;/a&gt; ) for generating todo ids.&lt;br&gt;
By default typescript does not know the v4 function returns a string.&lt;br&gt;
Let's see how we can help typescript understand external libraries !&lt;/p&gt;

&lt;h2&gt;
  
  
  External libraries
&lt;/h2&gt;

&lt;p&gt;For external libraries, there are usually 3 scenarios:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The library is written in typescript. When this is the case, most of the time we just need to &lt;code&gt;npm install&lt;/code&gt; the library and we have types. It is the best case scenario.&lt;/li&gt;
&lt;li&gt;The library does not ship directly with types. By default typescript does not know any types about the library. However, most of the time there is types written alongside the project. Usually, we can install those types using &lt;code&gt;npm install @types/[LIB_NAME]&lt;/code&gt;. This is the case for react.For example, there is a &lt;code&gt;@types/react&lt;/code&gt; package to add types with react.&lt;/li&gt;
&lt;li&gt;The library is not written with typescript and there is no types. This is the worst case scenario. You have to either write types yourself or use the &lt;code&gt;any&lt;/code&gt; types in typescript.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Note: As typescript is getting more and more popular, most of the time you will find types when using external libraries&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Todo app
&lt;/h2&gt;

&lt;p&gt;Let's get back to the &lt;a href="https://github.com/uuidjs/uuid" rel="noopener noreferrer"&gt;uuid&lt;/a&gt; package. uuid package is not written in typescript.&lt;br&gt;
However, there is a &lt;code&gt;@types/uuid&lt;/code&gt; package for it. The package is installed using &lt;code&gt;npm install --save-dev @types/uuid&lt;/code&gt;.&lt;br&gt;
This way when we assign an uuid to a todo's id, typescript knows, we are assigning a string to id.&lt;/p&gt;

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

&lt;p&gt;In my opinion, here are the pros and cons of using typescript with react.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pros&lt;/strong&gt; :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;By writing types when we writing components it forces us to think a more about our components and how it should be used&lt;/li&gt;
&lt;li&gt;If you have a compatible editor, typescript can give you error and autocompletion when you write code (even in JSX !)&lt;/li&gt;
&lt;li&gt;When you use or open a component file, you can see easily its parameters. You don't have to ask yourself "what is the name of this property, or if this property takes a string or a number"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cons&lt;/strong&gt; : &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It makes the code a bit more verbose. Since we need to specify types.&lt;/li&gt;
&lt;li&gt;It adds some complexity to build the project. We now need to transform typescript to javascript before running the apps. Hopefully tools like &lt;a href="https://create-react-app.dev/" rel="noopener noreferrer"&gt;cra&lt;/a&gt; provide a ready to use react/typescript template&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As I said, you can find the code of the todo app in &lt;a href="https://github.com/towaanu/typescript-react-todo-example/" rel="noopener noreferrer"&gt;this repo&lt;/a&gt;.&lt;br&gt;
I hope you like this little introduction to typescript with react! :) &lt;/p&gt;

</description>
      <category>react</category>
      <category>typescript</category>
      <category>javascript</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
