Your Static Site to a PWA!
Tags: webdev, pwa, javascript, html
Published: 2021-04-12 17:54:49

A ***Progressive Web App*** or ***PWA*** for short, is a website with added native capabilities. They are installable just like normal apps and even work offline and run in a separate browser process. They also support features like push notifications etc. PWAs have recently rose in importance because of the features they offer. 

> **_Fun Fact:_** DEV Community is also a PWA!

Building a basic PWA is not-so-difficult. Let's see how we can convert a static site into a PWA with a little changes.
***But First things First, understand a little more***

Additional Components necessary for a PWA:
  * A Manifest
  * A Service Worker
  * A HTTPS connection

Let us see their roles in building a PWA.
* The **_Manifest_** is a JSON file that describes the properties of our PWA. It contains information like app name, theme-color, icons, app shortcuts etc.
* The **_Service Worker_** is JS worker that runs in background process. It manages the state and cache of the app, handles notifications and syncs latest data with the offline one.
* HTTPS connection is needed for security purposes.

Here I am starting with a simple Clock made with the awesome trio (HTML, CSS, JS)

### Try the app first!!
{% glitch clockkk app %}
                                         
 <a href="https://clockkk.glitch.me">Click here</a> to be able to install it!

## !!! Note:
##### Not all browsers support PWA now but support will come soon. Latest versions of Chrome, FireFox, Edge support it recently.
#

Our project structure:
```
/Clock
    +-- /components
    |      +-- /images
    |      |      +-- favicon.png
    |      |      +-- clock-face.png
    |      +-- /scripts
    |      |      +-- main.js
    |      |      +-- pwa-handler.js
    |      +-- /styles
    |             +-- main.css
    +-- index.html
    +-- manifest.json
    +-- service-worker.js
```

> ***A-not-so-Fun Fact***: Adobe has discontinued their PhoneGap Build; which is a cloud service for building apps because of the rise of PWA.

#### ***index.html*** -> Remember to link the manifest!
```html
<!DOCTYPE html>
<html lang="en">
    <head>
        <!-- Some basic meta tags. -->
        <meta http-equiv="Content-Type" content="text/html;charset=UTF-8" />
        <meta http-equiv="X-UA-Compatible" content="IE=edge" />
        <meta name="viewport" content="width=device-width, height=device-height,user-scalable=no, initial-scale=1.0" />

        <!-- This one is important for the manifest. -->
        <meta name="theme-color" content="#0d085c" />

        <title>Clock</title>

        <!-- Reference the StyleSheet. -->
        <link rel="stylesheet" href="/components/main.css" />

        <!-- Reference the manifest file; is must for PWA. -->
        <link rel="manifest" href="/manifest.json" />

        <!-- Reference the icons -->
        <link rel="shorcut icon" href="/components/favicon.png" type="image/x-icon" />
        <link rel="apple-touch-icon" href="/components/favicon.png" />
    </head>
    <body>
        <div id="header">
            <div id="title">Clock</div>
            <div id="install">Install App</div>
        </div>

        <div id="clocks">
            <div id="analog">
                <div id="second"></div>
                <div id="minute"></div>
                <div id="hour"></div>
            </div>
            <div id="digital"></div>
        </div>

        <!-- Reference the main and helper scripts. -->
        <script src="/components/main.js"></script>
        <script src="/components/pwa-handler.js"></script>
    </body>
</html>
```

#### I have not shown the main.css and main.js files as they don't do much with the PWA but you could find them on the GitHub repo.

#### /components/scripts/***pwa-handler.js***
```js
// Reference the serviceWorker.
const serviceWorker = navigator.serviceWorker;

// Register our ServiceWorker script, if serviceWorker is available.
if (serviceWorker) {
    serviceWorker
        .register("/service-worker.js")
        .then(() => console.log("ServiceWorker Registered to the Application!"))
        .catch(() => console.log("Failed to Register the ServiceWorker."));
}

// Create a variable to defer the beforeinstallprompt event.
let beforeInstallEvent;

// Reference the install button from DOM.
const installButton = document.getElementById("install");

// Watch for the beforeinstallevent and defer it.
window.addEventListener("beforeinstallprompt", (event) => {
    event.preventDefault();
    beforeInstallEvent = event;
    installButton.style.display = "block";
});

// Prompt for Installation when install button is clicked.
installButton.addEventListener("click", () => {
    beforeInstallEvent
        .prompt()
        .then((choice) => {
            // Hide the install button as its purpose is over.
            if (choice.outcome == "accepted") {
                installButton.style.display = "none";
            }
        });
});
```
I have provided a button for prompting for installation in the `index.html` and it disappears as soon as the app is installed.

#### /components/images/***favicon.png***
![favicon.png](https://clockkk.glitch.me/components/images/favicon.png)

#### ***/components/images/clock-face.png***
![favicon.png](https://clockkk.glitch.me/components/images/clock-face.png)

#### ***manifest.json*** -> A 512x512 icon is a must!
```json
{
    "name": "Clock",
    "start_url": "/",
    "display": "standalone",
    "theme_color": "#0d085c",
    "icons": [
        {
            "src": "/components/images/favicon.png",
            "type": "image/png",
            "sizes": "512x512",
            "purpose": "any maskable"
        }
    ]
}
```
This is a bare-minimum manifest file; you will find a whole bunch of other properties that are available upon searching.

#### ***service-worker.js***
```js
// Name of the Cache.
const CACHE = "cacheV1";

// Select files for caching.
let urlsToCache = [
    "/",
    "/index.html",
    "/components",
    "/components/images",
    "/components/images/favicon.png", 
    "/components/images/clock-face.png",
    "/components/scripts",
    "/components/scripts/main.js", 
    "/components/scripts/pwa-handler.js",
    "/components/styles",
    "/components/styles/main.css"
];

// Cache all the selected items once application is installed.
self.addEventListener("install", (event) => {
    event.waitUntil(
        caches.open(CACHE).then((cache) => {
            console.log("Caching started.");
            return cache.addAll(urlsToCache);
        })
    );
});

// Whenever a resource is requested, return if its cached else fetch the resourcefrom server.
self.addEventListener("fetch", (event) => {
    event.respondWith(
        caches.match(event.request).then((response) => {
            if (response) {
                return response;
            }
            return fetch(event.request);
        })
    );
});
```
### With this much knowledge your are ready to dive into the world of PWA.

### Here is the GitHub repo:
{% github prorishi/Clock %}

### Thanks for your time and do Reference your PWAs in the discussions area.

> btw this is my first post ever on Internet!