Photo by Riccardo Annandale on Unsplash
PWA - Progressive Web Apps
How PWAs Deliver Native App Features on the Web?
Progressive Web Apps (PWAs) are a revolutionary approach to web development (single-page or, multi-page application) that combines the best of web and native apps. They offer an enhanced user experience by bringing features traditionally associated with native applications to the web, making them a powerful choice for modern app development e.g. working offline, having an icon of the app on the home screen, accessing the device camera or location, and syncing data in the background.
Examples of Successful PWAs: Twitter Lite, Pinterest, and Uber
Key Features of PWAs
Progressive: Works for every user.
Responsive: Fits any form factor, whether it’s a desktop, tablet, or smartphone.
Connectivity Independent: Works offline or on low-quality networks using service workers.
App-like: Feels like a native app with app-style interactions and navigation.
Fresh: Always up to date thanks to the service worker update process.
Safe: Served via HTTPS to ensure content is secure.
Re-engageable: Enables features like push notifications to keep users engaged.
Installable: Allows users to add the app to their home screen without the need for an app store.
Linkable: Easily shareable via URLs, without the complex installation process of native apps.
Benefits of Using PWAs
Cross-platform Compatibility: Build once, run everywhere, reducing development costs.
Enhanced Performance: Load faster and work offline, improving user satisfaction.
Lower Storage Requirements: Require minimal device storage compared to native apps.
Ease of Updates: Users always access the latest version without needing to download updates.
Core Blocks
Service Workers
Javascript is running in a background process, even if your application is closed.
Allow us to get offline access, cache some files, and serve them if we don't have an internet connection.
Give us access to other progressive web app-related features for example background synchronization, sending a request once the internet connection is re-established, Push notifications.
The application manifest file is this document that tells the browser more about our web application, makes the application installable on home screens
Responsive design is another core building block.
There are other things like the geolocation API or the camera API.
Manifest File (Enhancing Your PWA)
The manifest file is a JSON file that provides essential information about your Progressive Web App (PWA). It plays a crucial role in making your web application look and behave like a native app when installed.
Here are key properties you can define in the manifest file:
"name": Assign a name to your PWA. This name is displayed when the app is installed on the home screen.
"short_name": A shortened version of the app’s name, used when space is limited, such as below the app icon.
"start_url": Specifies the page to load on startup when the app icon is tapped.
"scope": Defines the range of URLs that fall under the PWA experience. For example, setting it to "." includes all pages in the web folder.
"display": Determines how the app is displayed after being added to the home screen. Options include:
"standalone" (default): Makes the app look like a native app by hiding browser controls.
"fullscreen" or "minimal-ui" for other variations.
"background_color": Specifies the background color displayed while the app is loading.
"theme_color": Sets the toolbar or task switcher color, helping the app blend with native environments.
"description": Provides a fallback description used in scenarios like bookmarking the app in a browser.
"dir": Sets text direction, e.g., "ltr" for left-to-right or "rtl" for right-to-left languages.
"lang": Specifies the app’s default language using a country code, such as "en-US."
"orientation": Controls the preferred screen orientation, e.g., "portrait" or "landscape."
"icons": Defines an array of icon objects for different device densities. Each object includes:
Path to the icon file.
MIME type (e.g., "image/png").
Icon dimensions (e.g., 48x48, 128x128, up to 512x512).
"related_applications": Lists related native applications. Each entry specifies:
The platform (e.g., "play" for Google Play Store).
The URL to the app in the store.
The app ID for identification.
Service Worker (The Backbone of PWAs)
Service workers are an essential component of Progressive Web Apps, enabling many of their key features. They are JavaScript files that run in the background and provide unique functionality beyond typical JavaScript code loaded by HTML pages.
Here’s how service workers work and what they offer:
Separate Thread: Service workers run on a separate single thread, decoupled from the main JavaScript thread used by your HTML pages. This ensures they can operate independently and perform tasks in the background.
Background Processes: Since they run in the background, service workers remain active even after all browser tabs of the application are closed. This allows them to listen for events such as push notifications or background synchronization.
HTTPS Requirement: Service workers only function over secure connections (HTTPS), ensuring a secure and tamper-proof environment for your application.
Event Listeners: They can listen to events triggered by JavaScript in your pages, HTML code, or even external servers. For instance, they can return cached assets for offline use or display notifications to users.
Scope of Operation: A service worker is registered through an HTML page, but once registered, it applies to all pages within its defined scope. For example, if a service worker is registered at the root of your domain, it will manage all pages under that domain.
Caching: Service workers can cache resources and serve them when the user is offline or on a slow network, improving performance and user experience.
By leveraging service workers, PWAs deliver robust functionality, ensuring seamless offline experiences, fast loading times, and re-engagement opportunities through features like push notifications.
Which Events Does the Service Worker Listen To?
Fetch Events:
These are HTTP requests triggered by the browser when loading assets like images.
Service workers intercept every fetch request sent by the browser, including those made using the
fetch
API in JavaScript.Note: Traditional AJAX requests (e.g., using
XMLHttpRequest
or libraries like Axios) do not trigger fetch events in the service worker.
Push Notifications:
Sent from a server to the browser's push service (e.g., Google for Chrome or Mozilla for Firefox).
The browser's push service delivers notifications to the service worker, which listens for incoming push events and reacts accordingly.
Notification Interaction:
After receiving a push notification, users might interact with it by clicking or tapping.
Service workers can listen for these interactions and execute relevant actions, even if the application tabs are closed.
Background Sync:
Some browsers support background synchronization, allowing queued actions to execute once the internet connection is restored.
The service worker listens for sync events and performs the stored actions when connectivity is re-established.
Service Worker Lifecycle
Installation (Install Event): During the registration of a service worker, an install event is emitted. This allows developers to execute code inside the service worker while it is getting installed, such as caching assets for offline use.
if ('serviceWorker' in navigator) { navigator.serviceWorker.register('/service-worker.js').then(() => { console.log('Service Worker Registered'); }); }
The
serviceWorker
property in thenavigator
object checks if the browser supports service workers. This ensures compatibility before executing service worker-related code.Activation (Activate Event): Once installed, the service worker emits an activate event. This is triggered only when there are no older versions of the service worker running. The activate event is an opportunity to clean up old caches or perform other initialization tasks.
self.addEventListener('activate', (event) => { console.log('Service Worker Activated'); });
Idle Mode and Termination: After activation, the service worker enters idle mode, sitting in the background waiting for events. If no events occur, it terminates to conserve resources. However, it wakes up automatically when new events, such as fetch requests or push notifications, are received.
Two Types of Cache
Service Worker Cache: Managed programmatically using the Cache API.
Default Browser Cache: Managed automatically by the browser.
Updating a Service Worker
A service worker is replaced only when a new version is available. The browser installs the new version and activates it when it’s safe to do so, ensuring a seamless experience for users. However , if a tab of the browser still open with the old service worker, the new one won’t be activated until all tabs using old sw are closed.
Which Events Does the Service Worker Listen To?
Fetch Events:
Service workers intercept fetch requests, allowing developers to serve cached responses or modify the request/response process.
self.addEventListener('fetch', (event) => { event.respondWith( caches.match(event.request).then((response) => response || fetch(event.request)) ); });
Note: Traditional AJAX requests (e.g.,
XMLHttpRequest
or libraries like Axios) do not trigger fetch events.Push Notifications:
Service workers handle push notifications sent from a server via a web push service. They enable features like sending timely updates to users even when the app is closed.
self.addEventListener('push', (event) => { const data = event.data.json(); self.registration.showNotification(data.title, { body: data.body, icon: data.icon, }); });
Notification Interaction:
React to user interactions with displayed notifications, such as clicking or dismissing them.
self.addEventListener('notificationclick', (event) => { event.notification.close(); event.waitUntil( clients.openWindow(event.notification.data.url) ); });
Background Sync:
Execute tasks when an internet connection is re-established, such as syncing data to a server.
self.addEventListener('sync', (event) => { if (event.tag === 'sync-data') { event.waitUntil(syncData()); } });
The best way to speed up a page is to load the assets from the cache and thus avoid network calls. This is really important when your users are on a slow network. This time to jump on to service workers and make full use of its caching strategies.
Caching strategies are
Cache first, Network fallback.
The service worker will loads the local (cached) assets (HTML, CSS, images, fonts, etc.), if possible, bypassing the network. If cached content is not available, the service worker returns a response from the web instead and caches the network response. This strategy can be used when dealing with remote resources that are very unlikely to change, such as static images.
The network first, Cache fallback.
In this strategy, the service worker will check the network first for a response and, if successful, returns current data to the page. If the network request fails, then the service worker returns the cached entry instead. Use this when data must be as fresh as possible, such as a real-time API response, but you still want to display something as a fallback when the network is unavailable.
Stale while revalidate
The stale-while-revalidate pattern allows you to respond to the request as quickly as possible with a cached response if available, falling back to the network request if it’s not cached. The network request is then used to update the cache. This is a fairly common strategy where having the most up-to-date resource is not vital to the application.
Network only
In this, the service worker will only check the network. There is no going to the cache for data. If the network fails, then the request fails. This can be used when only fresh data can be displayed on your site.
Cache only
The data is cached during the
install
event so that you can depend on the information being there.This can be useful if you have your own precaching step.
Conclusion
Progressive Web Apps are transforming how we think about application development. By combining the reach of the web with the capabilities of native apps, PWAs empower developers to create seamless, high-performing experiences that work on any device. Whether you’re building a new app or modernizing an existing one, PWAs are a compelling option for meeting user expectations in today’s digital age.