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!!
Click here 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!
<!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
// 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
/components/images/clock-face.png
manifest.json -> A 512x512 icon is a must!
{
"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
// 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:
Thanks for your time and do Reference your PWAs in the discussions area.
btw this is my first post ever on Internet!
Top comments (2)
Ok thanks for that but do you have an idea of how to create a PWA for a MVC server web app? JSP/ASP/Laravel etc etc
Sorry, I don't know about ASP or JSP but as far as I know your static assets could be made installable by adding a manifest and service worker. Maybe not all of your app will function but it could display another page while user is offline.