A Beginner's Guide To Progressive Web Apps

A Beginner's Guide To Progressive Web Apps

In this Progressive Web Apps (PWA) tutorial, you'll understand what a PWA is, why should You care? Why build native when you can build a progressive web app? Progressive web applications are a type of application software delivered through the web, built using common web technologies including HTML, CSS and JavaScript. They are intended to work on any platform that uses a standards-compliant browser.

Progressive web applications (PWAs) are a type of application software delivered through the web, built using common web technologies including HTML, CSS and JavaScript. They are intended to work on any platform that uses a standards-compliant browser.

WebKit, the tech underlying Safari and Mobile Safari, has recently (Aug 2017) declared that they’ve started working on introducing Service Workers into the browser. This means that soon they will land in iOS devices as well. So the Progressive Web Apps concept will likely be applicable to iPhones and iPads, if Apple decides to encourage this approach.

It’s not a groundbreaking new technology, but rather a new term that identifies a bundle of techniques that have the goal of creating a better experience for web-based apps.

What is a Progressive Web App

A Progressive Web App is an app that can provide additional features based on what the device supports, providing offline capability, push notifications, an almost native app look and speed, and local caching of resources.

This technique was originally introduced by Google in 2015, and proves to bring many advantages to both the developer and the users.

Developers have access to building almost-first-class applications using a web stack. This is always considerably easier and cheaper than building native applications, especially when considering the implications of building and maintaining cross-platform apps.

Devs can benefit from a reduced installation friction, and at a time when having an app in the store does not actually bring anything in terms of discoverability for 99,99% of the apps, Google search can provide the same benefits if not more.

A Progressive Web App is a website which is developed with certain technologies that make the mobile experience much more pleasant than a normal mobile-optimized website. It almost feels like working on a native app, as it offers the following features:

  • Offline support
  • Loads fast
  • Is secure
  • Is capable of emitting push notifications
  • Has an immersive, full-screen user experience without the URL bar

Mobile platforms (Android at the time of writing, but it’s not technically limited to that) offer increasing support for Progressive Web Apps. They even ask the user to add the app to the home screen when that user visits such a site.

But first, a little clarification on the name. Progressive Web App can be a confusing term, and a good definition is: web apps that take advantage of modern browsers features (like web workers and the web app manifest) to let their mobile devices “upgrade” the app to the role of a first-class citizen app.

Progressive Web Apps alternatives

How does a PWA stand compared to the alternatives when it comes to building a mobile experience?

Let’s focus on the pros and cons of each, and let’s see where PWAs are a good fit.

Native Mobile Apps

Native mobile apps are the most obvious way to build a mobile app. Objective-C or Swift on iOS, Java /Kotlin on Android and C# on Windows Phone.

Each platform has its own UI and UX conventions, and the native widgets provide the experience that the user expects. They can be deployed and distributed through the platform App Store.

The main pain point with native apps is that cross-platform development requires learning, mastering and keeping up to date with many different methodologies and best practices. If, for example, you have a small team or you’re a solo developer building an app on 3 platforms, you need to spend a lot of time learning the technology and environment. You’ll also spend a lot of time managing different libraries and using different workflows (for example, iCloud only works on iOS devices — there’s no Android version).

Hybrid Apps

Hybrid applications are built using Web Technologies, but are deployed to the App Store. In the middle sits a framework or some way to package the application so it’s possible to send it for review to the traditional App Store.

Some of the most common platforms are Phonegap and Ionic Framework, among many others, and usually what you see on the page is a WebView that essentially loads a local website.

The key aspect of Hybrid Apps is the write once, run anywhere concept. The different platform code is generated at build time, and you’re building apps using JavaScript, HTML and CSS, which is amazing. The device capabilities (microphone, camera, network, gps…) are exposed through JavaScript APIs.

The bad part of building hybrid apps is that, unless you do a great job, you might settle on providing a common denominator. This effectively creates an app that’s sub-optimal on all platforms because the app is ignoring the platform-specific human-computer interaction guidelines.

Also, performance for complex views might suffer.

Apps built with React Native

React Native exposes the native controls of the mobile device through a JavaScript API, but you’re effectively creating a native application, not embedding a website inside a WebView.

Their motto, to distinguish this approach from hybrid apps, is learn once, write anywhere. This means that the approach is the same across platforms, but you’re going to create completely separate apps in order to provide a great experience on each platform.

Performance is comparable to native apps, since what you build is essentially a native app which is distributed through the App Store.

Progressive Web Apps features

In the last section, you saw the main competitors of Progressive Web Apps. So how do PWAs stand compared to them, and what are their main features?

Remember — currently, Progressive Web Apps are for Android devices only.

Features

Progressive Web Apps have one thing that separates them completely from the above approaches: they are not deployed to the app store.

This is a key advantage. The app store is beneficial if you have the reach and luck to be featured, which can make your app go viral. But unless you’re in the top 0.001% you’re not going to get much benefit from having your little place on the App Store.

Progressive Web Apps are discoverable using Search Engines, and when a user gets to your site that has PWAs capabilities, the browser in combination with the device asks the user if they want to install the app to the home screen. This is huge, because regular SEO can apply to your PWA, leading to much less reliance on paid acquisition.

Not being in the App Store means you don’t need Apple’s or Google’s approval to be in the users pockets. You can release updates when you want, without having to go through the standard approval process which is typical of iOS apps.

PWAs are basically HTML5 applications/responsive websites on steroids, with some key technologies that were recently introduced to make some of the key features possible. If you remember, the original iPhone came without the option to develop native apps. Developers were told to develop HTML5 mobile apps that could be installed to the home screen, but the tech back then was not ready for this.

Progressive Web Apps run offline.

The use of service workers allow the app to always have fresh content, which can be downloaded in the background, and to provide support for push notifications, which offer greater re-engagement opportunities.

Also, sharability makes for a much nicer experience for users that want to share your app, as they just need a URL.

Benefits

So why should users and developers care about Progressive Web Apps?

  1. PWA are lighter. Native Apps can weigh 200MB or more, while a PWA could be in the range of the KBs.
  2. There’s no native platform code
  3. The cost of acquisition is lower (it’s much more difficult to convince a user to install an app than to visit a website to get the first-time experience)
  4. Significantly less effort is needed to build and release updates
  5. They have much more support for deep links than regular app-store apps
Core concepts
  • Responsive: the UI adapts to the device screen size
  • App-like feel: it doesn’t feel like a website but rather like an app (as much as possible)
  • Offline support: it will use the device storage to provide an offline experience
  • Installable: the device browser prompts the user to install your app
  • Re-engaging: push notifications help users re-discover your app once installed
  • Discoverable: search engines and SEO optimization can provide a lot more users than the app store
  • Fresh: the app updates itself and the content once it’s online
  • Safe: it uses HTTPS
  • Progressive: it will work on any device, even older one, even if it has fewer features (e.g. just as a website, not installable)
  • Linkable: it’s easy to point to it using URLs
Service Workers

Part of the Progressive Web App definition is that it must work offline.

Since the thing that allows the web app to work offline is the Service Worker, this implies that Service Workers are a mandatory part of a Progressive Web App.

WARNING: Service Workers are currently only supported by Chrome (Desktop and Android), Firefox, and Opera. See http://caniuse.com/#feat=serviceworkers for updated data on the support.

TIP: Don’t confuse Service Workers with Web Workers. They are a completely different thing.

A Service Worker is a JavaScript file that acts as a middleman between the web app and the network. Because of this it can provide cache services, speed the app rendering, and improve the user experience.

For security reasons, only HTTPS sites can make use of Service Workers, and this is part of the reason why a Progressive Web App must be served through HTTPS.

The App Manifest

The App Manifest is a JSON file that you can use to provide the device information about your Progressive Web App.

You add a link to the manifest in every header on each page of your web site:

<link rel="manifest" href="/manifest.webmanifest">

This file will tell the device how to set:

  • The name and short name of the app
  • The icons’ locations, in various sizes
  • The starting URL, relative to the domain
  • The default orientation
  • The splash screen

Example

{ 
  "name": "The Weather App", 
  "short_name": "Weather", 
  "description": "Progressive Web App Example", 
  "icons": [{
    "src": "images/icons/icon-128x128.png",
    "sizes": "128x128",
    "type": "image/png" 
  }, { 
    "src": "images/icons/icon-144x144.png",
    "sizes": "144x144", 
    "type": "image/png" 
  }, { 
    "src": "images/icons/icon-152x152.png",
    "sizes": "152x152", 
    "type": "image/png" 
  }, { 
    "src": "images/icons/icon-192x192.png",
    "sizes": "192x192", 
    "type": "image/png" 
  }, { 
    "src": "images/icons/icon-256x256.png", 
    "sizes": "256x256", 
    "type": "image/png" 
  }], 
  "start_url": "/index.html?utm_source=app_manifest", 
  "orientation": "portrait", 
  "display": "standalone", 
  "background_color": "#3E4EB8",
  "theme_color": "#2F3BA2" 
}

JavaScript Tutorial: if-else Statement in JavaScript

JavaScript Tutorial: if-else Statement in JavaScript

This JavaScript tutorial is a step by step guide on JavaScript If Else Statements. Learn how to use If Else in javascript and also JavaScript If Else Statements. if-else Statement in JavaScript. JavaScript's conditional statements: if; if-else; nested-if; if-else-if. These statements allow you to control the flow of your program's execution based upon conditions known only during run time.

Decision Making in programming is similar to decision making in real life. In programming also we face some situations where we want a certain block of code to be executed when some condition is fulfilled.
A programming language uses control statements to control the flow of execution of the program based on certain conditions. These are used to cause the flow of execution to advance and branch based on changes to the state of a program.

JavaScript’s conditional statements:

  • if
  • if-else
  • nested-if
  • if-else-if

These statements allow you to control the flow of your program’s execution based upon conditions known only during run time.

  • if: if statement is the most simple decision making statement. It is used to decide whether a certain statement or block of statements will be executed or not i.e if a certain condition is true then a block of statement is executed otherwise not.
    Syntax:
if(condition) 
{
   // Statements to execute if
   // condition is true
}

Here, condition after evaluation will be either true or false. if statement accepts boolean values – if the value is true then it will execute the block of statements under it.
If we do not provide the curly braces ‘{‘ and ‘}’ after if( condition ) then by default if statement will consider the immediate one statement to be inside its block. For example,

if(condition)
   statement1;
   statement2;

// Here if the condition is true, if block 
// will consider only statement1 to be inside 
// its block.

Flow chart:

Example:

<script type = "text/javaScript"> 

// JavaScript program to illustrate If statement 

var i = 10; 

if (i > 15) 
document.write("10 is less than 15"); 

// This statement will be executed 
// as if considers one statement by default 
document.write("I am Not in if"); 

< /script> 

Output:

I am Not in if
  • if-else: The if statement alone tells us that if a condition is true it will execute a block of statements and if the condition is false it won’t. But what if we want to do something else if the condition is false. Here comes the else statement. We can use the else statement with if statement to execute a block of code when the condition is false.
    Syntax:
if (condition)
{
    // Executes this block if
    // condition is true
}
else
{
    // Executes this block if
    // condition is false
}


Example:

<script type = "text/javaScript"> 

// JavaScript program to illustrate If-else statement 

var i = 10; 

if (i < 15) 
document.write("10 is less than 15"); 
else
document.write("I am Not in if"); 

< /script> 

Output:

i is smaller than 15
  • nested-if A nested if is an if statement that is the target of another if or else. Nested if statements means an if statement inside an if statement. Yes, JavaScript allows us to nest if statements within if statements. i.e, we can place an if statement inside another if statement.
    Syntax:
if (condition1) 
{
   // Executes when condition1 is true
   if (condition2) 
   {
      // Executes when condition2 is true
   }
}

Example:

<script type = "text/javaScript"> 

// JavaScript program to illustrate nested-if statement 

var i = 10; 

if (i == 10) { 

// First if statement 
if (i < 15) 
	document.write("i is smaller than 15"); 

// Nested - if statement 
// Will only be executed if statement above 
// it is true 
if (i < 12) 
	document.write("i is smaller than 12 too"); 
else
	document.write("i is greater than 15"); 
} 
< /script> 

Output:

i is smaller than 15
i is smaller than 12 too
  • if-else-if ladder Here, a user can decide among multiple options.The if statements are executed from the top down. As soon as one of the conditions controlling the if is true, the statement associated with that if is executed, and the rest of the ladder is bypassed. If none of the conditions is true, then the final else statement will be executed.
if (condition)
    statement;
else if (condition)
    statement;
.
.
else
    statement;


Example:

<script type = "text/javaScript"> 
// JavaScript program to illustrate nested-if statement 

var i = 20; 

if (i == 10) 
document.wrte("i is 10"); 
else if (i == 15) 
document.wrte("i is 15"); 
else if (i == 20) 
document.wrte("i is 20"); 
else
document.wrte("i is not present"); 
< /script> 

Output:

i is 20

How to Build a PWA from scratch with HTML, CSS, and JavaScript

How to Build a PWA from scratch with HTML, CSS, and JavaScript

In this article, we are going to build a PWA from scratch with HTML, CSS, and JavaScript. What is a Progressive Web App (PWA)? Progressive web apps are a way to bring that native app feeling to a traditional web app. With PWAs we can enhance our website with mobile app features which increase usability and offer a great user experience.

Progressive web apps are a way to bring that native app feeling to a traditional web app. With PWAs we can enhance our website with mobile app features which increase usability and offer a great user experience.

In this article, we are going to build a PWA from scratch with HTML, CSS, and JavaScript. Here are the topics we'll cover:

  • What is a Progressive Web App (PWA)?
  • Markup
  • Styling
  • Show data with JavaScript
  • Web App Manifest
  • What is a Service Worker?
  • Cache the assets
  • Fetch the assets
  • Register the Service Worker
  • Final thoughts

So, let's get started with an important question: What the heck is a PWA?

What is a Progressive Web App (PWA)?

A Progressive Web App is a web app that delivers an app-like experience to users by using modern web capabilities. In the end, it's just your regular website that runs in a browser with some enhancements. It gives you the ability:

  • To install it on a mobile home screen
  • To access it when offline
  • To access the camera
  • To get push notifications
  • To do background synchronization

And so much more.

However, to be able to transform our traditional web app to a PWA, we have to adjust it a little bit by adding a web app manifest file and a service worker.

Don't worry about these new terms – we'll cover them below.

First, we have to build our traditional web app. So let's start with the markup.

Markup

The HTML file is relatively simple. We wrap everything in the main tag.

  • In index.html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <link rel="stylesheet" href="css/style.css" />
    <title>Dev'Coffee PWA</title>
  </head>
  <body>
    <main>
      <nav>
        <h1>Dev'Coffee</h1>
        <ul>
          <li>Home</li>
          <li>About</li>
          <li>Blog</li>
        </ul>
      </nav>
      <div class="container"></div>
    </main>
    <script src="js/app.js"></script>
  </body>
</html>

And create a navigation bar with the nav tag. Then, the div with the class .container will hold our cards that we add later with JavaScript.

Now that we've gotten that out of the way, let's style it with CSS.

Styling

Here, as usual, we start by importing the fonts we need. Then we'll do some resets to prevent the default behavior.

  • In css/style.css
@import url("https://fonts.googleapis.com/css?family=Nunito:400,700&display=swap");
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}
body {
  background: #fdfdfd;
  font-family: "Nunito", sans-serif;
  font-size: 1rem;
}
main {
  max-width: 900px;
  margin: auto;
  padding: 0.5rem;
  text-align: center;
}
nav {
  display: flex;
  justify-content: space-between;
  align-items: center;
}
ul {
  list-style: none;
  display: flex;
}

li {
  margin-right: 1rem;
}
h1 {
  color: #e74c3c;
  margin-bottom: 0.5rem;
}

Then, we limit the main element's maximum width to 900px to make it look good on a large screen.

For the navbar, I want the logo to be at the left and the links at the right. So for the nav tag, after making it a flex container, we use justify-content: space-between; to align them.

  • In css/style.css
.container {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
  grid-gap: 1rem;
  justify-content: center;
  align-items: center;
  margin: auto;
  padding: 1rem 0;
}
.card {
  display: flex;
  align-items: center;
  flex-direction: column;
  width: 15rem auto;
  height: 15rem;
  background: #fff;
  box-shadow: 0 10px 20px rgba(0, 0, 0, 0.19), 0 6px 6px rgba(0, 0, 0, 0.23);
  border-radius: 10px;
  margin: auto;
  overflow: hidden;
}
.card--avatar {
  width: 100%;
  height: 10rem;
  object-fit: cover;
}
.card--title {
  color: #222;
  font-weight: 700;
  text-transform: capitalize;
  font-size: 1.1rem;
  margin-top: 0.5rem;
}
.card--link {
  text-decoration: none;
  background: #db4938;
  color: #fff;
  padding: 0.3rem 1rem;
  border-radius: 20px;
}

We'll have several cards, so for the container element it will be displayed as a grid. And, with grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)), we can now make our cards responsive so that they use at least 15rem width if there is enough space (and 1fr if not).

And to make them look nice we double the shadow effect on the .card class and use object-fit: cover on .card--avatar to prevent the image from stretching.

Now it looks much better – but we still don't have data to show.

Let's fix it in the next section

Show data with JavaScript

Notice that I used large images that take some time to load. This will show you in the best way the power of service workers.

As I said earlier, the .container class will hold our cards. Therefore, we need to select it.

  • In js/app.js
const container = document.querySelector(".container")
const coffees = [
  { name: "Perspiciatis", image: "images/coffee1.jpg" },
  { name: "Voluptatem", image: "images/coffee2.jpg" },
  { name: "Explicabo", image: "images/coffee3.jpg" },
  { name: "Rchitecto", image: "images/coffee4.jpg" },
  { name: " Beatae", image: "images/coffee5.jpg" },
  { name: " Vitae", image: "images/coffee6.jpg" },
  { name: "Inventore", image: "images/coffee7.jpg" },
  { name: "Veritatis", image: "images/coffee8.jpg" },
  { name: "Accusantium", image: "images/coffee9.jpg" },
]

Then, we create an array of cards with names and images.

  • In js/app.js
const showCoffees = () => {
  let output = ""
  coffees.forEach(
    ({ name, image }) =>
      (output += `
              <div class="card">
                <img class="card--avatar" src=${image} />
                <h1 class="card--title">${name}</h1>
                <a class="card--link" href="#">Taste</a>
              </div>
              `)
  )
  container.innerHTML = output
}

document.addEventListener("DOMContentLoaded", showCoffees)

With this code above, we can now loop through the array and show them on the HTML file. And to make everything work, we wait until the DOM (Document Object Model) content finishes loading to run the showCoffees method.

We've done a lot, but for now, we just have a traditional web app. So, let's change that in the next section by introducing some PWA features.

Web App Manifest

The web app manifest is a simple JSON file that informs the browser about your web app. It tells how it should behave when installed on the user's mobile device or desktop. And to show the Add to Home Screen prompt, the web app manifest is required.

Now that we know what a web manifest is, let's create a new file named manifest.jon (you have to name it like that) in the root directory. Then add this code block below.

  • In manifest.json
{
  "name": "Dev'Coffee",
  "short_name": "DevCoffee",
  "start_url": "index.html",
  "display": "standalone",
  "background_color": "#fdfdfd",
  "theme_color": "#db4938",
  "orientation": "portrait-primary",
  "icons": [
    {
      "src": "/images/icons/icon-72x72.png",
      "type": "image/png", "sizes": "72x72"
    },
    {
      "src": "/images/icons/icon-96x96.png",
      "type": "image/png", "sizes": "96x96"
    },
    {
      "src": "/images/icons/icon-128x128.png",
      "type": "image/png","sizes": "128x128"
    },
    {
      "src": "/images/icons/icon-144x144.png",
      "type": "image/png", "sizes": "144x144"
    },
    {
      "src": "/images/icons/icon-152x152.png",
      "type": "image/png", "sizes": "152x152"
    },
    {
      "src": "/images/icons/icon-192x192.png",
      "type": "image/png", "sizes": "192x192"
    },
    {
      "src": "/images/icons/icon-384x384.png",
      "type": "image/png", "sizes": "384x384"
    },
    {
      "src": "/images/icons/icon-512x512.png",
      "type": "image/png", "sizes": "512x512"
    }
  ]
}

In the end, it's just a JSON file with some mandatory and optional properties.

name: When the browser launches the splash screen, it will be the name displayed on the screen.

short_name: It will be the name displayed underneath your app shortcut on the home screen.

start_url: It will be the page shown to the user when your app is open.

display: It tells the browser how to display the app. There are several modes like minimal-ui, fullscreen, browser etc. Here, we use the standalone mode to hide everything related to the browser.

background_color: When the browser launches the splash screen, it will be the background of the screen.

theme_color: It will be the background color of the status bar when we open the app.

orientation: It tells the browser the orientation to have when displaying the app.

icons: When the browser launches the splash screen, it will be the icon displayed on the screen. Here, I used all sizes to fit any device's preferred icon. But you can just use one or two. It's up to you.

Now that we have a web app manifest, let's add it to the HTML file.

  • In index.html (head tag)
<link rel="manifest" href="manifest.json" />
<!-- ios support -->
<link rel="apple-touch-icon" href="images/icons/icon-72x72.png" />
<link rel="apple-touch-icon" href="images/icons/icon-96x96.png" />
<link rel="apple-touch-icon" href="images/icons/icon-128x128.png" />
<link rel="apple-touch-icon" href="images/icons/icon-144x144.png" />
<link rel="apple-touch-icon" href="images/icons/icon-152x152.png" />
<link rel="apple-touch-icon" href="images/icons/icon-192x192.png" />
<link rel="apple-touch-icon" href="images/icons/icon-384x384.png" />
<link rel="apple-touch-icon" href="images/icons/icon-512x512.png" />
<meta name="apple-mobile-web-app-status-bar" content="#db4938" />
<meta name="theme-color" content="#db4938" />

As you can see, we linked our manifest.json file to the head tag. And add some other links which handle the iOS support to show the icons and colorize the status bar with our theme color.

With that, we can now dive into the final part and introduce the service worker.

What is a Service Worker?

Notice that PWAs run only on https because the service worker can access the request and handle it. Therefore security is required.

A service worker is a script that your browser runs in the background in a separate thread. That means it runs in a different place and is completely separate from your web page. That's the reason why it can't manipulate your DOM element.

However, it's super powerful. The service worker can intercept and handle network requests, manage the cache to enable offline support or send push notifications to your users.

S0 let's create our very first service worker in the root folder and name it serviceWorker.js (the name is up to you). But you have to put it in the root so that you don't limit its scope to one folder.

Cache the assets

  • In serviceWorker.js
const staticDevCoffee = "dev-coffee-site-v1"
const assets = [
  "/",
  "/index.html",
  "/css/style.css",
  "/js/app.js",
  "/images/coffee1.jpg",
  "/images/coffee2.jpg",
  "/images/coffee3.jpg",
  "/images/coffee4.jpg",
  "/images/coffee5.jpg",
  "/images/coffee6.jpg",
  "/images/coffee7.jpg",
  "/images/coffee8.jpg",
  "/images/coffee9.jpg",
]

self.addEventListener("install", installEvent => {
  installEvent.waitUntil(
    caches.open(staticDevCoffee).then(cache => {
      cache.addAll(assets)
    })
  )
})

This code looks intimidating first but it just JavaScript (so don't worry).

We declare the name of our cache staticDevCoffee and the assets to store in the cache. And to perform that action, we need to attach a listener to self.

self is the service worker itself. It enables us to listen to life cycle events and do something in return.

The service worker has several life cycles, and one of them is the install event. It runs when a service worker is installed. It's triggered as soon as the worker executes, and it's only called once per service worker.

When the install event is fired, we run the callback which gives us access to the event object.

Caching something on the browser can take some time to finish because it's asynchronous.

So to handle it, we need to use waitUntil() which, as you might guess, waits for the action to finish.

Once the cache API is ready, we can run the open() method and create our cache by passing its name as an argument to caches.open(staticDevCoffee).

Then it returns a promise, which helps us store our assets in the cache with cache.addAll(assets).

Hopefully, you're still with me.

Now, we've successfully cached our assets in the browser. And the next time we load the page, the service worker will handle the request and fetch the cache if we are offline.

So, let's fetch our cache.

Fetch the assets

  • In serviceWorker.js
self.addEventListener("fetch", fetchEvent => {
  fetchEvent.respondWith(
    caches.match(fetchEvent.request).then(res => {
      return res || fetch(fetchEvent.request)
    })
  )
})

Here, we use the fetch event to, well, get back our data. The callback gives us access to fetchEvent. Then we attach respondWith() to prevent the browser's default response. Instead it returns a promise because the fetch action can take time to finish.

And once the cache ready, we apply the caches.match(fetchEvent.request). It will check if something in the cache matches fetchEvent.request. By the way, fetchEvent.request is just our array of assets.

Then, it returns a promise. And finally, we can return the result if it exists or the initial fetch if not.

Now, our assets can be cached and fetched by the service worker which increases the load time of our images quite a bit.

And most important, it makes our app available in offline mode.

But a service worker alone can't do the job. We need to register it in our project.

Register the Service Worker
  • In js/app.js
if ("serviceWorker" in navigator) {
  window.addEventListener("load", function() {
    navigator.serviceWorker
      .register("/serviceWorker.js")
      .then(res => console.log("service worker registered"))
      .catch(err => console.log("service worker not registered", err))
  })
}

Here, we start by checking if the serviceWorker is supported by the current browser (as it's still not supported by all browsers).

Then, we listen to the page load event to register our service worker by passing the name of our file serviceWorker.js to navigator.serviceWorker.register() as a parameter to register our worker.

With this update, we have now transformed our regular web app to a PWA.

Final thoughts

Throughout this article, we have seen how amazing PWAs can be. By adding a web app manifest file and a service worker, it really improves the user experience of our traditional web app. This is because PWAs are fast, secure, reliable, and – most importantly – they support offline mode.

Many frameworks out there now come with a service worker file already set-up for us. But knowing how to implement it with Vanilla JavaScript can help you understand PWAs.

And you can go even further with service workers by caching assets dynamically or limiting the size of your cache and so on.

Thanks for reading this article.

You can check it out live here and the source code is here.

Read more of my articles on this blog

Build a Basic PWA With Html and Javascript for Beginners

Build a Basic PWA With Html and Javascript for Beginners

A Progressive Web App (PWA) is a web app that uses modern web capabilities to deliver a native app-like experience to users. These apps meet certain requirements , are deployed to servers, accessible through URLs, and indexed by search engines. A Progressive Web App (PWA) works like any other normal app but with a lot of added features and a lot less hassle. They are fast, reliable, and can work perfectly in an offline environment.

A Progressive Web App (PWA) is a web app that uses modern web capabilities to deliver a native app-like experience to users. These apps meet certain requirements , are deployed to servers, accessible through URLs, and indexed by search engines.
A Progressive Web App (PWA) works like any other normal app but with a lot of added features and a lot less hassle. They are fast, reliable, and can work perfectly in an offline environment.

In this tutorial, we'll tell you the best way to to make the most straightforward application conceivable, that works without an internet connection, and can be added to your home screen.

You should know HTML, CSS, and JavaScript to get the most out of this tutorial. If you can code a web page and add some interactivity using plain-vanilla JavaScript, you should be able to follow. You'll need a text editor, the latest Chrome version and a local web server to build this app.

In this tutorial, we used Adobe's NetBeans, because it has a web server built, but you can use any text editor you’re comfortable with.

Creating a Basic PWA

1. The Setup

First of all, you have to create a directory for the app and add js, css, and images subdirectories. It should look like this when you’re finished:

/Hello-PWA   # Project Folder
/css     # Stylesheets
/js      # Javascript
/images  # Image files.

Open your project folder in Brackets to get started.

When writing the markup for a Progressive Web App there are 2 requirements to keep in mind:

  1. Even if JavaScript is disabled, the app should keep showing some content. This prevents users from viewing a blank page if their internet connection is poor or if an older browser is used.

  2. It should be responsive to a variety of devices and display properly. In other words, it must be mobile-friendly.

We will address the first requirement for our basic app by simply hard-coding the content and the second by adding a meta tag for the viewport. To do this, create an index.html file in the root folder of your project and add the following markup:


<!doctype html>
<html lang="en"> 
<head>   
<meta charset="utf-8">
<title>Hello World</title> 
<link rel="stylesheet" href="css/style.css">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<body class="fullscreen">
<div class="container">   
<h1 class="title">Hello World!</h1> 
</div>
</body>  
</html>

Second step: create a file named style.css in the css folder, such as:

body {font-family: sans-serif;} 
html, .fullscreen {display: flex;height: 100%; margin: 0;padding: 0;width: 100%; }  
.container {margin: auto;text-align: center;} 
.title {font-size: 3rem;}


The app can now be tested by clicking on the text editor preview button. This will open a browser window and serve up your page.

2. Testing

Now that we have our "Hello world" the browser, we're going to use Google's Lighthouse to test the app and see how well it meets PWA standards. Press F12 to open the Chrome developer panel and click on the Lighthouse audit tab

Make sure you check the option "Progressive Web App. " For now, you can uncheck the others. Then click on the button "run tests. " Lighthouse should give you a score and a list of audits that the app has passed or failed after a minute or two. At this point, the app should score about 45. If everything has been properly coded, you will notice that most of the tests carried out are related to the requirements we outlined at the beginning:

  1. If JavaScript is not available, our basic app contains some content.

  2. Has a wide or initial scale tag.

  3. The content of the viewport is correctly sized.

3. Add a Service Worker

Our app's next requirement is to register a service worker. Service workers are basically scripts that perform tasks that do not require user interaction in the background. This launches your users ' main app while the service worker takes care of mundane things.

For our app, we’ll use one to download and cache our content and then serve it back up from the cache when the user is offline.

Now, create a file named sw.js in the root folder and attempt to enter the contents of the following script. The reason it is stored in the root of the app is to give it access to all files of the app. This is because service workers only have access to files in the same directory and subdirectories.


var cacheName = 'hello-pwa'; 
var filesToCache = [
'/',    
'/index.html',    
'/css/style.css',  
'/js/main.js'  ];  
self.addEventListener('install', function(e) { 
e.waitUntil(
caches.open(cacheName).then(function(cache) { 
return cache.addAll(filesToCache);   
})    
);  
}); 
/* Serve cached content when offline */ 
self.addEventListener('fetch', function(e) {  
e.respondWith(      caches.match(e.request).then(function(response) {  
return response || fetch(e.request);
})   
);  
});


The script's first lines declare two variables: cacheName and files ToCache.

CacheName is used to create access of an offline cache from Javascript in the browser. FilesToCache is an array that contains a list of all cached files. These files should be written as URLs. Note that the first is just "/, "the base URL.

This means that the browser caches index.html even if the user does not type the file name directly.

Next, we add a function to install the service worker and use cacheName to create the browser cache. Once the cache has been created, all the files listed in the ToCache array will be added. Please keep in mind that while this code is used for demonstration purposes, it is not intended for production, as it will stop if even one of the files is not loaded.)

Finally, we add a function to load in the cached files when the browser is offline.

Finally, when the browser is offline, we add a function to load into the cached files.

We need to register the service worker with our app now.

In the js folder, create a file named main.js and enter the following code:


window.onload = () => {  
'use strict';     
if ('serviceWorker' in navigator) {     
navigator.serviceWorker  
.register('./sw.js'); 
} 
}


This code simply loads up the service worker script and gets it started.

Now, add the code to your app by adding the script to index.html just before the tag < /body > closes.

</div>
<script src="js/main.js"></script>
</body>

index.html should look like this:

<!doctype html>  
<html lang="en">  
<head>     
<meta charset="utf-8">  
<title>Hello World</title>  
<link rel="stylesheet" href="css/style.css">     
<meta name="viewport" content="width=device-width, initial-scale=1.0">   
</head>   
<body class="fullscreen">    
<div class="container">     
<h1 class="title">Hello World!</h1>   
</div>   
<script src="js/main.js"></script>   
</body>   
</html>

If you now run the Lighthouse audits, your score should rise to 64 and the service worker requirement passes.

4. Add a Manifest

The ultimate requirement for a PWA is a manifest file. The manifest is a json file that specifies how the app looks and acts on devices. For instance, the orientation and theme color of the app can be set.

In your root folder, save a file called manifest.json and add the following content:

{  
"name": "Hello World",   
"short_name": "Hello", 
"lang": "en-US",     
"start_url": "/index.html", 
"display": "standalone",  
"background_color": "white",   
"theme_color": "black" 
}


We set the title, background and theme colors for our app and tell the browser that it should be treated as an independent app without the chrome browser.

The fields are line by line as follows:

name
The app's title/name. This is used when the user is asked to install the app. It should be the app's full title.

short_name
Is the name of the app as shown on the icon app. It should be short.

lang
The default language the app is geo-localized (en-Us in our case)..

start_url
When the app is launched, tell the browser which page to load. It's generally index.html.

display
The shell type should be shown in the app. We use our app to look and feel like a standard native app on our own. Other settings are available to complete the screen or include the chrome browser.

background_color
The color of the splash screen that opens when the app launches.

theme_color
Sets the color of the toolbar and in the task switcher.

Now, add the manifest to your app:


<head>
...
<link rel="manifest" href="/manifest.json">
...
</head>

You should also declare the theme color to match the one set in your manifest by adding a meta tag inside the head:

<meta name="theme-color" content="black"/>


App Icons
You may have noticed that Lighthouse is complaining about missing app icons after the previous step. Although the app is not strictly required to work offline, it allows your users to add the app to their home screen.

Add the icons to your manifest file after the short_name property.

And now. add them to index.html.

The guide to this is the end! Hope you enjoyed this tutorial.

Learn More

Thanks for reading !