If you don't know what a PWA is or a service worker, take a look at my article here.
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/
Creating our PWA
1) Inside of your project's folder, you will create the following files:
2) Go to your index.html file and paste the following code:
<!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 here. The purpose of this tutorial is not to show you how to write css.
<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
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.
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:
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.
// 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
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.
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.
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 this 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
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:
{
"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 this 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
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 this website to generate yours.
# 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 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 tutorial.
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:
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.
Top comments (4)
Hi Pato!! Thank you for tutorial its really helpful. There was one thing I was wondering , how do we combine the
sw.js
you created for theAdd to Home Screen
PWA to the firebase push notifications one. Currently, both my service workers are overwriting each other so only one of them works.Just to clarify, I am registering both service workers in main.js, one after each other.
You can only have one service worker (besides the firebase-messaging-sw.js). In this code I have that functionality combined together github.com/devpato/creating-a-pwa-...
Thank you for the quick response! So just to confirm, with this, the
sw.js
is able to function properly and firebase is still able to talk to the background app without us registering it on our side?you will have the firebase-messaging-sw.js and the sw.js file like I did on the repo. Make sure the firebase-messaging-sw.js has that name. That's how firebase code will look for that file