Mobile DevelopmentWednesday, January 14, 2026

Building a Progressive Web App (PWA) from Scratch

Braine Agency
Building a Progressive Web App (PWA) from Scratch

Building a Progressive Web App (PWA) from Scratch

```html Build a PWA From Scratch: A Comprehensive Guide | Braine Agency

Welcome to a comprehensive guide on building a Progressive Web App (PWA) from scratch, brought to you by Braine Agency. In today's mobile-first world, PWAs are revolutionizing how users interact with web applications. They offer the best of both worlds: the discoverability of the web and the immersive experience of native apps. This guide will walk you through each step, from initial setup to deployment, empowering you to create a powerful and engaging PWA.

What is a Progressive Web App (PWA)?

A Progressive Web App (PWA) is a web application that uses modern web capabilities to deliver an app-like experience to users. Think of it as a website that can be installed on a user's device, offering features like offline access, push notifications, and a native app feel. PWAs are designed to be:

  • Reliable: Load instantly and never show the downasaur, even in uncertain network conditions.
  • Fast: Respond quickly to user interactions with smooth animations and no janky scrolling.
  • Engaging: Feel like a natural app on the device, with an immersive user experience.

According to Google, users spend 87% of their time in apps, but mobile websites reach 3x more people. PWAs bridge this gap by offering app-like experiences on the web. PWAs have shown significant improvements in engagement and conversions. For example, Starbucks saw a 2x increase in daily active users after launching their PWA.

Why Build a PWA?

There are numerous benefits to choosing a PWA over a traditional website or native app:

  • Increased Engagement: Push notifications and offline access keep users coming back.
  • Improved Performance: PWAs are designed to be fast and responsive, leading to a better user experience.
  • Lower Development Costs: Building a single PWA can be more cost-effective than developing separate native apps for iOS and Android.
  • Enhanced Discoverability: PWAs are easily discoverable through search engines.
  • Automatic Updates: Users always have the latest version without needing to manually update.
  • Reduced Storage Space: PWAs typically take up less storage space than native apps.

Prerequisites

Before we dive in, make sure you have the following:

  • Basic knowledge of HTML, CSS, and JavaScript.
  • A code editor (e.g., VS Code, Sublime Text).
  • A web browser with developer tools (e.g., Chrome, Firefox).
  • Node.js and npm (Node Package Manager) installed (optional, but recommended for tooling).

Step-by-Step Guide to Building a PWA

1. Setting up Your Project

First, create a new directory for your PWA project. Inside this directory, create three basic files:

  • index.html: The main HTML file for your PWA.
  • style.css: The CSS file for styling your PWA.
  • script.js: The JavaScript file for adding interactivity and PWA functionality.

Here's a basic index.html file:


    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>My PWA</title>
        <link rel="stylesheet" href="style.css">
        <link rel="manifest" href="manifest.json">
    </head>
    <body>
        <h1>Welcome to My PWA!</h1>
        <p>This is a basic PWA example.</p>
        <script src="script.js"></script>
    </body>
    </html>
    

And a basic style.css file:


    body {
        font-family: sans-serif;
        text-align: center;
    }
    

And an empty script.js file for now.

2. Creating the Manifest File (manifest.json)

The manifest file is a JSON file that provides information about your PWA to the browser. It includes details like the app's name, icons, and start URL. Create a file named manifest.json in your project directory.


    {
        "name": "My PWA",
        "short_name": "My PWA",
        "start_url": "/",
        "display": "standalone",
        "background_color": "#ffffff",
        "theme_color": "#000000",
        "icons": [
            {
                "src": "icon-192x192.png",
                "sizes": "192x192",
                "type": "image/png"
            },
            {
                "src": "icon-512x512.png",
                "sizes": "512x512",
                "type": "image/png"
            }
        ]
    }
    

Explanation:

  • name: The full name of your PWA.
  • short_name: A shorter version of the name, used on the home screen.
  • start_url: The URL that opens when the user launches the PWA.
  • display: Defines how the app should be displayed (standalone removes browser UI). Other options include `fullscreen`, `minimal-ui`, and `browser`.
  • background_color: The background color of the splash screen.
  • theme_color: The color of the browser UI (e.g., the address bar).
  • icons: An array of icon objects, specifying different sizes and types. You'll need to create these icon files (e.g., icon-192x192.png and icon-512x512.png) and place them in your project directory. There are online tools that can help you generate these icons from a single image.

Make sure to link the manifest file in your index.html file, as shown in the example above: <link rel="manifest" href="manifest.json">

3. Implementing a Service Worker

Service workers are the heart of PWAs. They are JavaScript files that run in the background, separate from the main browser thread. They enable features like:

  • Offline Access: Caching resources so the app can work even without an internet connection.
  • Push Notifications: Sending notifications to users even when the app is not open.
  • Background Sync: Performing tasks in the background, even when the app is closed.

Create a file named service-worker.js in your project directory. Here's a basic example:


    const CACHE_NAME = 'my-pwa-cache-v1';
    const urlsToCache = [
        '/',
        '/index.html',
        '/style.css',
        '/script.js',
        '/icon-192x192.png',
        '/icon-512x512.png'
    ];

    self.addEventListener('install', event => {
        event.waitUntil(
            caches.open(CACHE_NAME)
                .then(cache => {
                    console.log('Opened cache');
                    return cache.addAll(urlsToCache);
                })
        );
    });

    self.addEventListener('fetch', event => {
        event.respondWith(
            caches.match(event.request)
                .then(response => {
                    // Cache hit - return response
                    if (response) {
                        return response;
                    }

                    // Not in cache - fetch from network
                    return fetch(event.request).then(
                        function(response) {
                            // Check if we received a valid response
                            if(!response || response.status !== 200 || response.type !== 'basic') {
                                return response;
                            }

                            // IMPORTANT: Clone the response. A response is a stream
                            // and because we want the browser to consume the response
                            // as well as the cache consuming the response, we need
                            // to clone it so we have two independent copies.
                            var responseToCache = response.clone();

                            caches.open(CACHE_NAME)
                                .then(function(cache) {
                                    cache.put(event.request, responseToCache);
                                });

                            return response;
                        }
                    );
                })
        );
    });

    self.addEventListener('activate', event => {
        const cacheWhitelist = [CACHE_NAME];

        event.waitUntil(
            caches.keys().then(cacheNames => {
                return Promise.all(
                    cacheNames.map(cacheName => {
                        if (cacheWhitelist.indexOf(cacheName) === -1) {
                            return caches.delete(cacheName);
                        }
                    })
                );
            })
        );
    });
    

Explanation:

  • CACHE_NAME: A name for your cache. Increment this when you update the service worker to force a cache refresh.
  • urlsToCache: An array of URLs to cache when the service worker is installed.
  • install event: This event is triggered when the service worker is installed. It opens the cache and adds the specified URLs to it.
  • fetch event: This event is triggered when the browser makes a network request. It checks if the requested resource is in the cache. If it is, it returns the cached resource. Otherwise, it fetches the resource from the network, caches it, and returns it.
  • activate event: This event is triggered when a new service worker is activated. It cleans up any old caches.

Now, you need to register the service worker in your script.js 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);
                });
        });
    }
    

This code checks if the browser supports service workers and, if so, registers the service-worker.js file.

4. Testing Your PWA

To test your PWA, you need to serve it over HTTPS. Browsers require HTTPS for service workers to function properly. You can use a local development server like http-server (from npm) or serve it using a tool like Parcel or Webpack. Here's how to use `http-server`:

  1. Install http-server: npm install -g http-server
  2. Navigate to your project directory in the terminal.
  3. Run http-server: http-server
  4. Open your browser and navigate to the address provided by http-server (usually http://localhost:8080).

Open your browser's developer tools (usually by pressing F12) and go to the "Application" tab (in Chrome) or "Storage" tab (in Firefox). You should see the "Service Workers" section. Verify that your service worker is registered and running.

You can also test the "Add to Home Screen" functionality. In Chrome, click on the three dots in the top-right corner, then "More tools" > "Create shortcut...". Check the "Open as window" option and click "Create". This will install your PWA as a standalone app.

5. Adding Push Notifications (Optional)

Push notifications can significantly increase user engagement. Implementing push notifications requires a few more steps:

  1. Get a Firebase Cloud Messaging (FCM) API key: You'll need an FCM API key to send push notifications. Create a Firebase project and get the API key from the Firebase console.
  2. Add the Firebase SDK to your project: Include the Firebase SDK in your index.html file.
  3. Modify your service worker to handle push notifications: Add code to your service worker to listen for push notification events and display the notifications.
  4. Request permission from the user to send push notifications: Use the Push API to request permission from the user.

Implementing push notifications is a more advanced topic and beyond the scope of this basic guide. However, there are many excellent tutorials and libraries available online that can help you get started.

Advanced PWA Features

Once you have a basic PWA up and running, you can explore more advanced features:

  • Background Sync: Allows you to perform tasks in the background, even when the app is closed. Useful for things like queuing data updates.
  • Web Share API: Enables users to easily share content from your PWA to other apps.
  • Web Bluetooth API: Allows your PWA to communicate with nearby Bluetooth devices.
  • Payment Request API: Simplifies the payment process for users.
  • Credential Management API: Makes it easier for users to log in to your PWA.

PWA Use Cases

PWAs are suitable for a wide range of applications:

  • E-commerce: PWAs can provide a fast and engaging shopping experience, leading to increased conversions. Many retailers like Alibaba have successfully implemented PWAs.
  • News and Media: PWAs can deliver news and articles quickly and reliably, even offline.
  • Social Media: PWAs can provide a native app-like experience for social media users.
  • Productivity Tools: PWAs can be used to create powerful productivity tools that work seamlessly across devices.

Here are some real-world examples of successful PWAs:

  • Twitter Lite: Twitter Lite is a PWA that provides a fast and data-efficient way to access Twitter.
  • Forbes: Forbes' PWA offers a fast and engaging reading experience.