Offline web applications are becoming more and more popular. Offline support is so important that it’s now common to talk about the ‘Offline First’ approach, where it becomes a primary consideration. It’s also gathering popularity with the rise of Progressive Web Apps.
In this post, we will look at how to add offline support to a basic contact list web app by implementing asset caching, client-side data storage and synchronization with a remote data store.
The source code of the app is available on GitHub.
Why Support Offline?
Why should we care about offline support?
I myself spend more than one hour on a train every day. I don’t want to waste this time, so I take my laptop to work a bit along the way. I use the cell network to be online. The connection is not reliable, so I lose it from time to time. My user experience is up to the web app I’m using. Only a few apps, with good offline support, behave as expected and connection loss is transparent. Some behave weirdly, so when I refresh the page, I lose data. Most don’t support offline at all, and I have to wait for a stable connection to be able to use them.
An unreliable connection is not the only use case. We can also talk about situations where you might be offline for several hours, for example, while on an airplane.
Another important advantage of offline support is the performance boost. Indeed, the browser doesn’t need to wait for assets to be loaded from the server. The same for data, once stored on the client.
Thus we need offline:
to be able to use apps even with flaky connection (cell network in a train)
to be able to work without network connection (on an airplane)
to boost the performance
Progressive Web Apps
Google’s Progressive Web Apps (PWA) concept is a methodology aimed at delivering web apps that provide the UX of native mobile apps. PWA includes offline support, but it also covers a lot more:
Responsiveness – support for different form factors: mobile, tablet, desktop
Web App Manifest – to install an app on the home screen
App Shell – a design pattern in which the basic UI app shell is separated from the content loaded afterward
Push notifications – to get “instant" updates from the server
Addy Osmani wrote a great intro post about PWA.
In this article, we are going to focus only on a single aspect: offline support.
Defining Offline Support
Let’s clarify what it takes to support offline. We need to take care of two aspects:
app assets – caching HTML, JS scripts, CSS style sheets, images
app data – storing data on client-side
The first solution in HTML5 to cache offline assets was AppCache. The idea is to provide an app manifest describing which resources should be stored in the browser cache. Thus, the next time an app is loaded, these assets will be taken from the browser cache.
Important: While being simple, there are quite a lot of pitfalls with using AppCache. The standard is now deprecated, although it’s still widely supported by browsers.
Service Workers were introduced to replace AppCache. They provide a flexible solution for the offline support. Service Workers give control over outgoing requests, allowing a script intercept them and return the necessary responses. The caching logic is entirely on the developer’s shoulders. The app code itself can check if an asset is saved in the cache and requests it from the server only if needed.
It’s important to note that Service Workers are supported only via HTTPS (HTTP is allowed for localhost) connections. We will look at how to use Service Workers shortly.
App data can be stored in the offline storage provided by browsers.
There are several options introduced by HTML5:
WebStorage – key-value storage
IndexedDB – NoSQL database
WebSQL – built-in SQLite database
WebStorage is a key-value storage. This is the simplest cross-browser storage, but there are several pitfalls to be aware of. You have to take care of serialization and deserialization of data that you put inside because the values must be plain strings. You may run up against size limits with larger data sets. Also, it’s possible to get into a race condition, meaning if you have two tabs opened at the same time in the browser you could end up with unexpected behavior.
IndexedDB is much more powerful and seems to be the best way to go with offline storage. It has plenty of space available. It supports transactions and can be safely used in several browser tabs at the same time. It’s also supported by all modern browsers.
WebSQL is literally SQLite in the browser. Full-featured relational DB with ACID on the client. Unfortunately, WebSQL has been deprecated by the standards committee and was never supported in non-Blink/Webkit browsers.
There are several libraries out there which provide an abstraction over offline storage:
localForage – simple localStorage-like API
IDBWrapper – cross-browser IndexedDB wrapper
PouchDB – client-side storage solution inspired by CouchDB. It supports automatic sync with the backend if CouchDB is being used.
The ContactBook App
Now, let’s see how to add offline support to a web application. Our sample app is a basic contact book:
We have the list of contacts on the left and a details form on the right used for editing contacts. A contact has three fields: first name, last name, and phone.
You can find the app source code on GitHub. To run the app you’ll need Node.js installed. If you’re not sure about this step, you can follow our beginner’s guide to npm.
Start by downloading the sources and running the following commands from the project folder:
$ npm install
$ npm run serve
What about backend? We are using pouchdb-server to provide a REST API over CouchDB storage, and http-server to serve frontend assets.
Continue reading %Create Offline Web Apps Using Service Workers & PouchDB%