Vue.js and Progressive Web Apps (PWAs): Building Offline-First Applications

Blessing Mba
12 min readDec 12, 2023
Photo by Mohammad Rahmani on Unsplash

Vue.js is a progressive model-view frontend framework used in building user interfaces and single-page applications. Vue.js and a Progressive Web App (PWA) is a powerful combination for developing Offline-First applications.

A Progressive Web App(PWA) is a web application that leverages both web-based and native mobile technologies, to provide an app-like user experience, without requiring users install an app from an app store. Like a website, PWA can run on multiple platforms and devices from a single codebase. Examples of some Progressive Web Apps are Instagram, Spotify, Uber, and Pinterest.

Benefits of Progressive web apps over native and traditional web apps

  • Cost-effective Development: Progressive web apps are more cost-effective to develop. Native web apps require more resources and different requirements for different operating systems, including app store maintenance fees, but PWAs are built with HTML, CSS, and JavaScript, which is a cheaper alternative.
  • Offline Functionality: Progressive web apps work offline, unlike native web apps. They are independent of the network, which enables users to access and interact with the application even without an active Internet connection.
  • Optimized for Search Engines and Accessibility: They are designed to be highly optimized for search engines, ensuring better visibility in search results. Additionally, they prioritize accessibility features, enhancing user experience for a diverse audience.
  • Advanced Security Measures: They provide built-in security benefits, and they are built with HTTPs that encrypt data shared between app and server, which makes it difficult to hack to access sensitive data. Progressive web apps have limited permissions, which reduces their exposure to security threats compared to native web apps.
  • Optimized Performance and Seamless Cross-Device Experience: Progressive web apps are lightweight, more responsive, have faster load time, and provide a seamless user experience across various devices.

Importance of offline-first applications in modern web development

  • Offline Accessibility: Users can access web applications offline, which enhances uptime and accessibility.
  • Seamless Synchronization: Changes made offline are easily synced when there’s a network connection which prevents data loss.
  • Enhanced Performance: Offline-first applications provide a better performance and faster load times.
  • Network Independence: It allows users to use applications at their convenience and with poor internet connection.
  • Uninterrupted Access: Users can securely store data and access important features while offline with a poor network connection.
    In modern web development, understanding the importance of offline-first applications is crucial. Users can access web apps while offline and make changes that sync when the network connection is restored. Tools like Workbox, Service Workers, IndexedDB, and Web Manifest play important roles in implementing offline-first features in web applications.

Workbox

Offline functions in Progressive web apps are implemented with service workers. Service workers handle lots of integrations like cache strategies, network management, network requests, precaching, and more. These complex integrations can be tricky. Workbox is a set of JavaScript libraries built to solve this complexity and aims at making service workers as easy as possible.

  • Workbox manages asset caching and serving needs.
  • Workbox helps in updating assets if they change in the server.
  • Workbox manages background data synchronization and precaching.

Progressive Web App configuration using Workbox

These are steps to take to configure your PWA using Workbox.

  • Add PWA support to your Vue app by using a Vue CLI plugin for PWA. Install by running this command in your terminal
vue add pwa
  • Configure the PWA, during installation, you will be prompted to configure your options like icons, theme colors, name, and background color.
  • By default, the Vue CLI plugin will set up a basic service worker using Workbox.
  • Register your service worker in a JavaScript file.
if('serviceWorker' in navigator) {
window.addEventListener('load',() => {
navigator.serviceWorker.register('/service-worker.js').then
registration => {console.log('Service Worker registered with
scope:',registration.scope);
}).catch(error => {console.error('Service Worker registration
failed:',error);
});
});
}
  • After configuration, build your Vue app:
npm run build 

Service workers for offline-first applications

A Service worker plays a pivotal role in an offline-first application. It is an event-driven worker registered against an origin and a path. It takes the form of a JavaScript file that can control the web page/site that it is associated with, intercepting and modifying navigation and resource requests, and caching resources in a very granular fashion to give you complete control over how your app behaves in certain situations like one being when the network is not available.

Importance of Service Workers

  • Caching Assets: They reduce loading times and bandwidth usage by caching assets such as HTML, Javascript, fonts, and images.
  • Assets Accessibility: They make it possible to properly cache assets and make them available when the user's device is offline.
  • Enhanced Performance: Service workers optimize the performance of a website.
  • Network Request Management: They intercept and handle network requests for a seamless user experience.

Caching strategies

Caching involves storing frequently accessed data or content in a temporary storage location called a cache. This enables faster loading times. By reducing the time it takes to access frequently requested content, web applications respond to user requests quickly which enables a seamless and rich user experience. In an Offline-First application, implementing effective caching strategies is vital for optimizing performance and ensuring a smooth user experience. Here’s an overview of different caching strategies.
Different caching strategies

Within web development, different caching strategies play a pivotal role in optimizing performance. From efficient asset caching to robust network request management, understanding these strategies is key to delivering a seamless user experience. Caching strategies include;

  • Write-through: In this strategy, When data is updated, it is written to the cache and the database simultaneously. This ensures that the cache always contains up-to-date data. It reverses the order of how the cache is populated.
  • Write-Behind: Data is first written to the cache and later to the database in this strategy. The cache layer itself connects to the backing database.
  • Read through: In this strategy, when data is requested, the cache is checked first. The cache is the primary source of data and when the data is not found in the cache, it is retrieved and stored in the cache for future use.
  • Cache-aside: In this strategy, when data is requested, the application checks the cache first. The application is responsible for managing the cache.

Workbox implements these caching strategies by providing a range of service workers tools. It enables the precaching of assets for offline use, manages cache storage, and simplifies the implementation of caching strategies, which provides an improved user experience.

Using Workbox for precaching critical assets in your project

  • Define the list of critical assets you want to precache in your service worker. Workbox provides a precacheAndRoute method to make this process easy. For instance
import { precacheAndRoute } from ‘workbox-precaching’;
precacheAndRoute([
{ url: '/index.html', revision: 'v1' },
{ url: '/styles.css', revision: 'v1' },
{ url: '/app.js', revision: 'v1' },
// Add more assets here
]);

You can use various cache strategies like cacheFirst, networkFirst, or staleWhileRevalidate to determine how resources are fetched and served. Workbox will prompt you to configure these strategies.

  • In your service worker, intercept network requests and check if the requested resource is in the cache. If it's in the cache, serve it from there; otherwise, fetch it from the network and cache it. Workbox provides APIs to help with this process.

Workbox helps in precaching critical assets, ensuring they are available for offline use, which provides a smooth user experience.

Dynamic caching of resources at runtime and strategies with Workbox.
Dynamic caching of resources at runtime is important for handling data that is personalized for each user or data that changes frequently. This ensures data is updated.

Here are steps to take to cache resources dynamically:

  • Use the workbox. routing.registerRoute method to define routes for dynamic resources. For example, API endpoints or user-generated content.
  • Choose a cache strategy that suits your needs, such as cacheFirst, networkFirst, or staleWhileRevalidate.
  • Cache the response from the network or other data sources as needed
import { register route } from ‘workbox-routing’;
import { CacheFirst, NetworkFirst, StaleWhileRevalidate } from ‘workbox-strategies’;
// Cache dynamic content using a specific strategy
registerRoute(
/api\/data/,
new NetworkFirst({
cacheName: 'dynamic-data',
})
);
.
  • To be able to make decisions on a per-request basis and cache resources, implement custom caching logic within the service worker using the fetch event.
self.addEventListener('fetch', (event) => {
// Implement custom caching logic based on the request and response
event.respondWith(yourCustomCachingLogic(event.request));
});
  • Ensure that you version your caches to handle updates for your dynamic resources by using Workbox's expiration features to remove outdated data from the cache.
import { CacheFirst } from 'workbox-strategies';
registerRoute(
/api\/data/,
new CacheFirst({
cacheName: 'dynamic-data',
plugins: [
new workbox.expiration.Plugin({
maxEntries: 50, // Limit the number of entries
maxAgeSeconds: 60 * 60, // Cache for 1 hour
}),
],
})
);

Using Workbox for background synchronization of data in a PWA

Background synchronization of data ensures that data is synchronized with a server while offline or with a poor network connection. This can be done with these steps.

  • Ensure a service worker is set up and registered
  • Workbox provides a background plugin for data synchronization
import { registerRoute } from 'workbox-routing';
import { NetworkOnly } from 'workbox-strategies';
import { Queue } from 'workbox-background-sync';
const queue = new Queue('syncQueue', {
onSync: async ({ queue }) => {
let entry;
while ((entry = await queue.shiftRequest())) {
try {
await fetch(entry.request);
// Data successfully synchronized
} catch (error) {
// Handle synchronization errors
await queue.unshiftRequest(entry);
}
}
},
});
registerRoute(
/\/api\/sync/,
new NetworkOnly({
plugins: [
{
fetchDidFail: async ({ request }) => {
await queue.pushRequest({ request });
},
},
],
})
);
  • In this example, we created a Queue for background synchronization with a name (’syncQueue’). The onSync event is triggered when the browser is online and can be used to process queued requests. A defined route triggers network-only requests to your synchronization API endpoint.
    If a request fails due to a network issue, it is pushed into the queue for later synchronization. It is important to handle synchronization errors by pushing back failed requests into the queue.
  • To trigger background sync, call the registerSync method when you’re online.
if ('serviceWorker' in navigator && 'SyncManager' in window) {
navigator.serviceWorker.ready.then((registration) => {
registration.sync.register('syncData');
});
}

With these steps, when PWA is offline, Workbox will queue the data requests which will be processed immediately network connection is available. Data requests are processed immediately when PWA is online. This provides a rich and seamless user experience.

Using IndexedDB for data storage and retrieval in offline-first applications with Workbox.

While Workbox serves and caches assets, IndexedDB handles data storage and retrieval in a PWA. By combining Workbox for asset caching and IndexedDB for structured data storage, you can create a powerful offline-first PWA. It provides an improved and seamless application performance.

Follow the steps below to implement IndexedDB with Workbox.

  • Ensure that your service worker is set and registered.
  • Create an indexedDB in your workbox for structured data storage.


self.addEventListener('install', (event) => {
const dbPromise = indexedDB.open('myDatabase', 1);
dbPromise.onupgradeneeded = (event) => {
const db = event.target.result;
db.createObjectStore('myData', { keyPath: 'id' });
};
});
  • Store the data in the IndexedDB for offline access, whenever your PWA fetches data from the network.


self.addEventListener('fetch', (event) => {
event.respondWith(
fetch(event.request)
.then((response) => {
if (response.status === 200) {
const data = response.clone().json();
storeDataInIndexedDB(data); // Custom function to store data
}
return response;
})
.catch((error) => {
// Handle fetch errors
return new Response('Offline Data Placeholder');
})
);
});
  • Retrieve data from IndexedDB and serve it to the user, when PWA is offline.
self.addEventListener('fetch', (event) => {
event.respondWith(
fetch(event.request)
.catch(() => {
return retrieveDataFromIndexedDB(event.request);
})
);
});
  • Implement synchronization logic that periodically checks for network connectivity and updates the data in IndexedDB to match the latest data from the server.

Web Manifest

A web app manifest is a JSON file that tells the browser about your Progressive Web App and how it should behave when installed on the user’s desktop or mobile device. It provides information that the browser needs to install a progressive web app (PWA) on a device, such as the app’s name and icon.

Setting up a Web manifest

  • In your Vue.js project, create a manifest.json file in the public directory.
  • Open the manifest.json file and define the properties for your PWA. Modify these properties according to your PWA’s details, including the name, description, start URL, display mode, and color.
{
"name": "My Vue.js PWA",
"short_name": "My PWA",
"description": "A Vue.js Progressive Web App",
"start_url": "/",
"display": "standalone",
"background_color": "#ffffff",
"theme_color": "#000000",
"orientation": "portrait",
"icons": [
{
"src": "/img/icons/icon-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/img/icons/icon-512x512.png",
"sizes": "512x512",
"type": "image/png"
}
]
}
  • Add a link to the manifest file to the HTML ‘head’ section of your Vue app.


<link rel="manifest" href="/manifest.json">

Techniques for improving PWA performance
Optimizing Progressive Web App's(PWA) performance is crucial for a smooth user experience. This section explores methods for faster loading and effective data management to achieve outstanding results.

  • Ordering network requests: Order network requests in parallel and the most critical ones are made upfront.
  • Apply Lazy Loading, this helps improve your PWA’s performance by reducing the amount of data that is initially being requested and loaded, allowing the page to load faster while saving on bandwidth.
  • Client-side caching: Eliminate duplicated network requests by caching the response within your connector.
  • Avoid web fonts: Web fonts compete with network resources, which slows down loading. Use standard web fonts instead. These are fonts that are already supported on your users’ systems and don’t require an additional download.
  • Provide Optimized Images: Providing the optimal image size for the device in use can help reduce the amount of unnecessary information in the request.

Best Practices for a Seamless User Experience in an offline-first PWA

  • Precaching critical assets during installation, to ensure fast loading times when offline.
  • Optimize assets.
  • Use IndexedDB for data storage and retrieval.
  • Implement background synchronization of using Workbox.
  • Monitor network status and adjust your app accordingly .
  • Implement client-side form validation and submit data locally. This form syncs when the network connection is restored.
  • Implement efficient error handling for data synchronization failures.
  • Test your PWA during offline, poor network connection, and online conditions.

Testing offline and online conditions of your PWA
Testing your app is a crucial step to ensure your app works in different network scenarios.
Here are steps you can take to test for both offline and online conditions of your PWA

  • Use a network simulation tool to mimic offline and online conditions. One such tool is the "Network" tab in your browser’s developer tools.
  • Implement code to detect the online status of the app. You can use the navigator.onLine property to determine if the app is currently online or offline.
if (navigator.onLine) {
// The app is online
} else {
// The app is offline
}
  • Design an offline UI that displays when the app is offline. This can be a custom message or cached content.

<div id="offline-message" style="display: none;">
You are currently offline. Some features may be limited.
</div>
  • Implement offline-first features that work seamlessly when offline, like displaying cached data, offline forms, or offline access to certain sections of the app.

Testing for offline condition
Simulate an offline condition using the network simulation tool in your browser. Disable the network, and then observe how your app behaves. Ensure that the offline UI is displayed and that any offline-first features work as expected.

Testing for online condition
Enable the network in the network simulation tool to return to an online state. Verify that the app synchronizes with the server, and the offline UI is hidden. Check if real-time data updates work correctly.

  • Test service worker caching and updates by changing the content of your app, forcing a service worker update, and verifying that the app loads the updated content when online.
  • Trigger background synchronization of data while the app is online and verify that the synchronization occurs. Then, simulate offline conditions and check if the synchronization is queued and takes place once the network is restored.
  • Consider using browser emulators or tools like Lighthouse for more complex testing. They can simulate various network conditions and provide detailed reports on PWA performance.

Techniques and tools for debugging PWA features

  • Debugging is essential in Progressive Web Apps development, it resolves problems associated with offline functionality, error handling, service workers, and data storage and retrieval
    Some of the tools for debugging PWA features include;
  • Browser Developer Tools: You can access these tools by pressing F12 or Ctrl+Shift+I (Windows/Linux) or Cmd+Option+I (macOS). The console helps you view errors and messages. Network feature helps you inspect network requests including service worker interactions.
  • Lighthouse: This is an open-source tool that audits your pages for performance, accessibility, and SEO, and offers suggestions so you can improve these aspects.
  • Workbox debugging tools
  • Offline Testing
  • Cross-browser Testing
  • Error handling
  • Network Testing

Conclusion
Building a Vue.js and Progressive Web App (PWA) based Offline-First applications offers an improved user experience with seamless functionality, irrespective of network connectivity. Leveraging Vue.js’s reactivity along with PWA’s caching features allows for instant data access, resulting in a responsive, dependable, and efficient web app, ultimately boosting user engagement and content access even in offline conditions.

--

--