How To Create A PWA With JavaScript
Tags: pwa, javascript, webdev, tutorial
Published: 2020-02-17 16:54:18

If you don't know what a PWA is or a service worker, take a look at my article <a href="https://dev.to/thisdotmedia/intro-to-pwa-and-service-workers-15d4">here</a>.

**Level:** Beginner
**Requirements:** Javascript, HTML, CSS, and a good attitude.

Now, let's jump into creating a PWA from SCRATCH!

## LIVE DEMO

https://my-first-pwa-744ee.firebaseapp.com/

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/kd1rya5z9q93vquyu1tf.png)

## Creating our PWA

1) Inside of your project's folder, you will create the following files:
<img src="https://dev-to-uploads.s3.amazonaws.com/i/ibxhpyqnfe0a5i3iogrk.png" />

2) Go to your index.html file and paste the following code:

```html
<!DOCTYPE html>
<html lang="en">
  <head>
    <link rel="manifest" href="manifest.json">
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <meta name="theme-color" content="#222" />
    <meta name="description" content="My First PWA">
    <meta name="apple-mobile-web-app-capable" content="yes">
    <meta name="apple-mobile-web-app-status-bar-style" content="black">
    <meta name="apple-mobile-web-app-title" content="My First PWA">
    <link rel="apple-touch-icon" href="/icons/icon-152x152.png">
    <title>My First PWA</title>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
    <link rel="stylesheet" type="text/css" href="./styles/styles.css" />
  </head>
  <body>
    <!-- More before code here -->
    <script src="./js/index.js"></script>
  </body>
</ lang="en">
```

I feel the meta tags are pretty self-explanatory. If you want to dig more into them, just go to google.com :)

If you take a look at the code, you can see I'm importing the **manifest.json**, the **styles.css** and the **index.js**, so you don't have to worry about doing that, especially the **manifest.json**. You probably haven't seen this file before and its very important.

3) Inside of your index.html where you have the body, we will create our responsive navbar.

**FYI:** This navbar was taken from <a href="https://www.w3schools.com/howto/howto_js_topnav_responsive.asp">here</a>. The purpose of this tutorial is not to show you how to write css.

```html
<div class="navbar" id="navbar">
  <a href="#home" class="active">Home</a>
  <a href="#news">News</a>
  <a href="#contact">Contact</a>
  <a href="#about">About</a>
   <a href="javascript:void(0);" alt="button to toggle menu" aria-label="button to toggle menu" class="icon" onclick="toggleMenu()">
    <i class="fa fa-bars"></i>
  </a>
</div>
```

4)Time to add some colors to our app inside our **styles.css**

```css
body {
  margin: 0;
  font-family: Arial, Helvetica, sans-serif;
}

.navbar {
  overflow: hidden;
  background-color: #333;
}

.navbar a {
  float: left;
  display: block;
  color: #f2f2f2;
  text-align: center;
  padding: 14px 16px;
  text-decoration: none;
  font-size: 17px;
}

.navbar a:hover {
  background-color: #ddd;
  color: black;
}

.navbar a.active {
  background-color: #c3042f;
  color: #fff;
}

.navbar .icon {
  display: none;
}

@media screen and (max-width: 600px) {
  .navbar a:not(:first-child) {
    display: none;
  }
  .navbar a.icon {
    float: right;
    display: block;
  }
}

@media screen and (max-width: 600px) {
  .navbar.responsive {
    position: relative;
  }
  .navbar.responsive .icon {
    position: absolute;
    right: 0;
    top: 0;
  }
  .navbar.responsive a {
    float: none;
    display: block;
    text-align: left;
  }
}

```

5) **index.js** file paste the following code that will be doing the opening and closing of our responsive menu.

```javascript
function toggleMenu() {
  const navbar = document.getElementById("navbar");
  if (navbar.className === "navbar") {
    navbar.className += " responsive";
  } else {
    navbar.className = "navbar";
  }
}
```

## Service Worker Lifecyle

### Register

6) Inside of your **index.js**, you will register your service worker as followed:

```javascript
if ("serviceWorker" in navigator) {
  navigator.serviceWorker.register("sw.js").then(() => {
    console.log("[ServiceWorker**] - Registered");
  });
}
```

### Install

7) Inside of your service worker (sw.js) file, you will add the following code to install your service worker. Then we are going to cache some of our assets. The caching will take place during the install of your service worker.

**Note:** Besides caching, you can do way more things during the install.

```javascript
// Service Worker
const cacheName = "my-first-pwa";
const filesToCache = [
  "/",
  "index.html",
  "./js/index.js",
  "./styles/styles.css"
];

self.addEventListener("install", e => {
  console.log("[ServiceWorker**] Install");
  e.waitUntil(
    caches.open(cacheName).then(cache => {
      console.log("[ServiceWorker**] Caching app shell");
      return cache.addAll(filesToCache);
    })
  );
});

```

If you want to check the browser to ensure your Service Worker got registered, go to Google Dev Tool -> Application -> Service Worker

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/gqqmgjyqrmhq7dos5nbg.png)

### Activate

8) Inside of your service worker (sw.js) file, you will add the following code. In this code snippet, we wait for an activate event and then run a waitUntil() block that clears up any old/unused caches before a new service worker is activated. Here we have a whitelist containing the names of the caches we want to keep. We return the keys of the caches in the CacheStorage object using keys(), then check each key to see if it is in the whitelist. If not, we delete it using CacheStorage.delete.

```javascript
self.addEventListener("activate", event => {
  caches.keys().then(keyList => {
    return Promise.all(
      keyList.map(key => {
        if (key !== cacheName) {
          console.log("[ServiceWorker] - Removing old cache", key);
          return caches.delete(key);
        }
      })
    );
  });
});
```

### Cache falling back to the network

9) If you're making your app offline-first, this is how you'll handle the majority of requests. Other patterns will be exceptions based on the incoming request.

```javascript
self.addEventListener("fetch", event => {
  event.respondWith(
    caches.match(event.request, { ignoreSearch: true }).then(response => {
      return response || fetch(event.request);
    })
  );
});
```

To learn about different fallback approaches for your scenarios visit <a href="https://developers.google.com/web/ilt/pwa/caching-files-with-service-worker">this</a> google docs

### Validating our cached files

If you want to see that your files have been cached, all you have to do is go to the Google Dev tools->Application->Cache Storage


![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/dp6rb61besdkz6a7xwtm.png)

## Manifest.json

This file is a file that tells the browser some basic information about your PWA and how your app going to behave once it has been installed on the user's device. 

Have you ever gone to a website and it asks you **"Add to Home Screen"?** well this is thanks to the **manifest.json**.

10) Inside of you **manifest.json** paste the following code:
```json
{
  "short_name": "My PWA",
  "name": "My First PWA",
  "icons": [
    {
      "src": "./icons/icon.png",
      "type": "image/png",
      "sizes": "192x192"
    },
    {
      "src": "./icons/icon.png",
      "type": "image/png",
      "sizes": "512x512"
    }
  ],
  "start_url": "/index.html",
  "background_color": "#222",
  "display": "standalone",
  "scope": "/",
  "theme_color": "#c3042f"
}
```

To learn more about the **manifest.json** visit <a href="https://developers.google.com/web/fundamentals/web-app-manifest/">this</a> docs.

Don't forget that we **added** the manifest in the html ;)

11) In the root of your project, create a folder called "icons". Then add whatever icon you want with the name **icon.png**. If the icon has another name, you will have to update the **manifest.json**

**Note:** You can get some icons from FlatIcons https://www.flaticon.com/.

12) Verify the **manifest.json** is being detected. Once again, go to the Google Dev Tools -> Application -> Manifest

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/63c1qlzin7gnlcmtnank.png)

Once you run this app in your browser, it should ask you if you want to add the PWA to the home screen. 

### Robots.txt file

13) Inside of this Robots.txt file you can enter the following code or you can visit <a href="https://smallseotools.com/robots-txt-generator/">this</a> website to generate yours. 

```javascript
# robots.txt generated by smallseotools.com
User-agent: Googlebot
Disallow: 
User-agent: *
Disallow: 
Disallow: /cgi-bin/
```

This file is good for bots and SEO. If you want to learn more about them, take a look at this article:

https://www.cloudflare.com/learning/bots/what-is-robots.txt/



### Sitemap

Add the sitemap code inside of the **sitemap.xml**. You can generate one here: https://www.xml-sitemaps.com/ or use mine and just change the URL.

```xml
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd">
<!-- Generated by Web-Site-Map.com -->
<url><loc>https://my-first-pwa-744ee.firebaseapp.com/</loc><lastmod>2020-01-20T23:50:35+00:00</lastmod><changefreq>always</changefreq><priority>1.00</priority></url>
</urlset>
```


### Deploy your PWA. 

I'm not going to get into this again, but you can deploy the PWA wherever you want or you can do it via Firebase by following my other tutorial <a href="https://dev.to/thisdotmedia/web-app-deployment-with-firebase-in-2-minutes-4bo8">tutorial</a>. 


### The Lighthouse Report

Lighthouse is a tool that will help get some metrics for our app. The Lighthouse report can be generated by adding the Google Chrome extension. Lighthouse will generate a report that will test your app for performance, accessibility, best practices, SEO, and if it’s a PWA.

In order to run the report, go to your PWA website and click on the Lighthouse Chrome extension and wait for the magic. You should see something like this:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/eek5ior9hvg0ojh67es5.png)


**Note** The Robots.txt file and the sitemap.xml aren't needed to make your app a PWA but they are good to have and will make your school from Lighthouse higher.

<img src="https://thepracticaldev.s3.amazonaws.com/i/5a87eog40zvcsimuaew4.png" />