Nowadays, users install on average zero mobile applications per month. This means users install fewer native applications on their mobile devices. Since the web has a reach of three times more than native applications without compromising and requiring the users to install them, the perfect solution would be something that users can access with their web browser and if they want to install it on their devices (if they don’t they can always access it with their browser). This is what Progressive Web Applications (introduced by Google) try to achieve. They are just web applications that try to behave like native applications: work offline, receive push notifications, have good performance, etc.
The brain behind this new concept is the service workers: JavaScript files that act as a proxy in your web application. They can intercept the requests (and cache them), listen for events, and do something when they happen. For example, we can cache certain assets when the service worker is installed and serve them directly through the service worker instead of making a trip to grab those assets every time they are needed.
Why Use a Service Worker?
Caching
The headline offer is the faster loading of assets and offline support via caching. Service workers can act as a middleware between your browser and external content (like images, fonts, APIs, etc.) by intercepting HTTP requests made by your browser. This can lead to a number of different strategies for returning assets to users such as initially showing low-res images before swapping in the correct version or implementing a stale-while-revalidate pattern for regularly updated content (e.g. a game leaderboard or news feed).
Background Sync
Storing content in a cache has some drawbacks — what if the app provides an offline display for a customer’s orders and the content is out of date? Thankfully, background sync allows service workers to listen for specific sync events triggered by an application so any cached content can be updated ready for consumption either immediately or when the device is next online.
Background sync also allows us to handle poor network conditions when a user is trying to submit some data back to service. Instead of forcing users to wait while a slow request is sent or having to inform them that a request failed because the network connection was down it’s possible to use sync to complete the request in the background when connectivity comes back.
Push Notifications
Push notifications have had a huge impact on native application engagement rates but traditionally have been out of reach for web applications. The Push_API enables native-style push notifications to be received from a server, whether or not the web app is in the foreground (or even loaded).
There’s a lot of literature around on the web to support the use of asynchronous messaging to raise engagement rates with app users so this will be a big step forward for web apps once browser support is better. Speaking of which…
Browser Support
Service worker support is gradually improving, with full support in Chrome, Firefox and Opera and partial support in MS Edge. Webkit has signaled that they are under consideration with meeting notes from 2015 saying:
Service Workers — becoming a more frequent request. We should do it
How Do Service Workers Work?
Service worker scripts are independent and have their own life cycle: The script first registered and then installed by the browser. After that, it starts caching static assets right away. If any errors occur, then installation will fail, and you must try again. For a successful installation, the service worker will activate and gain control over all pages under its scope. Active service workers alternate between two states: either it will handle fetch and message events, or it will terminate to save memory.
Service Worker Caching: Service worker caching is much more manageable. It always checks network status whether it is online or offline and fetches or terminates the resource files accordingly. This means the site can work when there is no network, offline, or poor network connectivity, low bars, or false cellular connectivity. Supported Browsers Browser options are growing. Service workers are supported by Chrome, Firefox, and Opera. Microsoft Edge is now showing public support. Even Safari has dropped hints of future development.
Restrictions and Security:
• Service Worker can’t access the window object. That’s why it does not have any threat for spreading viruses.
• Require HTTPS. This ensures a level of security to allow the service worker to do the things it is designed to do.
• Only Asynchronous. This means synchronous APIs like XHR and local Storage are not available to a service worker.
Service Worker Implementation:
By default a Service Worker is scoped to ./ from the path on which it is loaded. For example, in our Next.js application a service worker loaded from app.com/static/sw.js would be scoped to the /static path.
Ideally ,the service worker to be registered would be loaded from https://my.app.com/sw.js
Creating a Basic Service Worker
For the purposes of this article, the service worker implementation is going to be extremely simple (purely dynamic caching the index page of the application). The following code was created in a file public/sw.js
try {
const PRECACHE = "precache-v2";
const RUNTIME = "runtime";
// A list of local resources we always want to be cached.
const PRECACHE_URLS = [
`any url`, // Alias for index.html
];
// The install handler takes care of precaching the resources we always need.
self.addEventListener("install", (event) => {
console.log("installing sw");
event.waitUntil(
caches
.open(PRECACHE)
.then((cache) => cache.addAll(PRECACHE_URLS))
.then(self.skipWaiting())
);
});
// The activate handler takes care of cleaning up old caches.
self.addEventListener("activate", (event) => {
const currentCaches = [PRECACHE, RUNTIME];
console.log("activate cache");
event.waitUntil(
caches
.keys()
.then((cacheNames) => {
return cacheNames.filter(
(cacheName) => !currentCaches.includes(cacheName)
);
})
.then((cachesToDelete) => {
console.log("cache is deleting");
return Promise.all(
cachesToDelete.map((cacheToDelete) => {
return caches.delete(cacheToDelete);
})
);
})
.then(() => self.clients.claim())
);
});
// The fetch handler serves responses for same- origin resources from a cache.
// If no response is found, it populates the runtime cache with the response
// from the network before returning it to the page.
self.addEventListener("fetch", (event) => {
// Skip cross-origin requests, like those for Google Analytics.
if (event.request.url.startsWith(self.location.origin)) {
event.respondWith(
caches.match(event.request).then((cachedResponse) => {
if (cachedResponse) {
return cachedResponse;
}
return caches.open(RUNTIME).then((cache) => {
return fetch(event.request, {
}).then((response) => {
// Put a copy of the response in the runtime cache.
return cache.put(event.request, response.clone()).then(() => {
return response;
});
});
});
})
);
}
});
} catch (e) {
console.log(e);
}
All this code does is listen for the install event and when triggered it pre-fetches all the URLs specified in precache_urls. Then it adds a fetch listener to intercept requests to content under the scope of the service worker and, if present, returns the pre-fetched response from the cache.
Then, in the ./pages/index.js component the service worker needs to be registered (if supported).
class Index extends React.Component {
componentDidMount = () => {
if ("serviceWorker" in navigator) {
window.addEventListener("load", function () {
navigator.serviceWorker.register("/sw.js").then(
function (registration) {
console.log(
"Service Worker registration successful with scope: ",
registration.scope
);
},
function (err) {
console.log("Service Worker registration failed: ", err);
}
);
});
}
render()
{
return <div> hello </div>
}
}
To Conclude…
We hope that through this article, you will be able to understand how you can integrate service workers and Next.js. At Codersera, we take care of new technologies taking over, drawing comparisons between different languages, installations and integrations, and more.