coderain guide

How to Build a Progressive Web App with React

PWAs are web apps that use modern browser APIs to deliver a native-like experience. Key features include: - **Offline Support**: Work without an internet connection using service workers. - **Installable**: Users can add the app to their home screen (no app store required). - **Responsive**: Adapt to any screen size (mobile, tablet, desktop). - **Fast**: Cached assets and optimized loading for quick interactions. - **Secure**: Served over HTTPS to protect user data. React simplifies PWA development with tools like `create-react-app` (CRA), which includes built-in PWA templates and integrations with Workbox (a library for service worker management).

Progressive Web Apps (PWAs) bridge the gap between web and native mobile apps, offering features like offline support, installability, and fast load times—all while remaining accessible via a browser. React, with its component-based architecture and robust ecosystem, is an excellent framework for building PWAs. In this guide, we’ll walk through creating a production-ready PWA with React, covering core concepts, tools, and best practices.

Table of Contents

  1. Introduction to PWAs
  2. Prerequisites
  3. Setting Up a React PWA Project
  4. Understanding PWA Fundamentals
  5. Step 1: Configure the Web App Manifest
  6. Step 2: Implement Service Workers
  7. Step 3: Test with Lighthouse
  8. Step 4: Add Offline Functionality
  9. Step 5: Enable Installability
  10. Advanced PWA Features
  11. Deployment
  12. Troubleshooting Common Issues
  13. Conclusion
  14. References

Prerequisites

Before starting, ensure you have:

  • Node.js (v14+ recommended) and npm/yarn installed.
  • Basic knowledge of React (components, hooks, state management).
  • A code editor (e.g., VS Code).
  • A modern browser (Chrome, Firefox, or Edge) for testing.

Setting Up a React PWA Project

We’ll use create-react-app with the official PWA template to bootstrap our project. This template preconfigures the Web App Manifest and service worker setup.

Step 1: Create the Project

Run the following command in your terminal:

npx create-react-app my-react-pwa --template cra-template-pwa  

This creates a new React app with:

  • A public/manifest.json file (for PWA metadata).
  • A src/service-worker.js file (for offline logic, powered by Workbox).
  • A src/serviceWorkerRegistration.js file (to register the service worker).

Project Structure

Key files for PWAs in the generated project:

my-react-pwa/  
├── public/  
│   ├── manifest.json       # PWA metadata (icons, name, display mode)  
│   ├── icons/              # App icons for different devices  
├── src/  
│   ├── service-worker.js   # Service worker logic (caching, offline support)  
│   ├── serviceWorkerRegistration.js  # Registers the service worker  
│   └── index.js            # Entry point (registers the service worker)  

Understanding PWA Fundamentals

Before diving into code, let’s clarify the two core technologies powering PWAs:

1. Web App Manifest

A JSON file (manifest.json) that tells the browser about your app’s identity and appearance. It enables features like:

  • Adding the app to the home screen.
  • Customizing the app’s name, icons, and theme color.
  • Defining the display mode (e.g., standalone for a full-screen, app-like experience).

2. Service Workers

A script that runs in the background (separate from the browser tab) to handle:

  • Caching assets (HTML, CSS, JS, images) for offline use.
  • Intercepting network requests to serve cached content.
  • Syncing data in the background when connectivity is restored.

React’s PWA template uses Workbox, a library that simplifies service worker configuration (e.g., precaching static assets, defining caching strategies).

Step 1: Configure the Web App Manifest

The manifest.json file is critical for PWA installability and branding. Let’s customize it for our app.

Default manifest.json

The CRA PWA template generates a basic manifest. Open public/manifest.json and update the fields below:

{  
  "short_name": "ReactPWA",  # Shown on home screen/launcher  
  "name": "My React PWA",    # Full name (e.g., in app stores)  
  "description": "A sample PWA built with React",  # Optional but recommended  
  "start_url": ".",          # URL to load when the app is launched  
  "display": "standalone",   # Display mode: "standalone", "fullscreen", or "minimal-ui"  
  "background_color": "#ffffff",  # Color shown while the app loads  
  "theme_color": "#4285f4",       # Color for browser UI (e.g., address bar)  
  "icons": [  
    {  
      "src": "icons/icon-72x72.png",  
      "sizes": "72x72",  
      "type": "image/png"  
    },  
    {  
      "src": "icons/icon-96x96.png",  
      "sizes": "96x96",  
      "type": "image/png"  
    },  
    {  
      "src": "icons/icon-128x128.png",  
      "sizes": "128x128",  
      "type": "image/png"  
    },  
    {  
      "src": "icons/icon-144x144.png",  
      "sizes": "144x144",  
      "type": "image/png"  
    },  
    {  
      "src": "icons/icon-152x152.png",  
      "sizes": "152x152",  
      "type": "image/png"  
    },  
    {  
      "src": "icons/icon-192x192.png",  
      "sizes": "192x192",  
      "type": "image/png"  
    },  
    {  
      "src": "icons/icon-384x384.png",  
      "sizes": "384x384",  
      "type": "image/png"  
    },  
    {  
      "src": "icons/icon-512x512.png",  
      "sizes": "512x512",  
      "type": "image/png"  
    }  
  ]  
}  

Key Manifest Fields Explained

  • display: "standalone": Hides the browser toolbar, making the app look like a native app.
  • icons: Provide multiple sizes (72x72 to 512x512) to support different devices (phones, tablets, desktops). Use tools like RealFaviconGenerator to generate icons.
  • theme_color and background_color: Ensure consistency with your app’s design.

Step 2: Implement Service Workers

Service workers are the backbone of offline support. The CRA PWA template uses Workbox to automate caching, but we’ll customize it for our needs.

How Service Workers Work in React

By default, the template uses workbox-precaching to cache static assets (HTML, CSS, JS) during the build process. This ensures assets load quickly and offline.

Registering the Service Worker

The service worker must be registered in your app’s entry point (src/index.js). The template includes this logic via serviceWorkerRegistration.register():

// src/index.js  
import React from 'react';  
import ReactDOM from 'react-dom/client';  
import './index.css';  
import App from './App';  
import * as serviceWorkerRegistration from './serviceWorkerRegistration';  

const root = ReactDOM.createRoot(document.getElementById('root'));  
root.render(  
  <React.StrictMode>  
    <App />  
  </React.StrictMode>  
);  

// Register service worker for production (comment out for development)  
serviceWorkerRegistration.register();  

Customizing the Service Worker

To add runtime caching (e.g., for API responses), modify src/service-worker.js. Workbox provides strategies like StaleWhileRevalidate (serve cached data first, then update) or NetworkFirst (fetch from network, fall back to cache).

Example: Cache API Responses

Add the following to src/service-worker.js to cache data from an API endpoint (e.g., https://api.example.com/data):

// src/service-worker.js  
import { precacheAndRoute } from 'workbox-precaching';  
import { registerRoute } from 'workbox-routing';  
import { StaleWhileRevalidate } from 'workbox-strategies';  

// Precache static assets (auto-generated by CRA during build)  
precacheAndRoute(self.__WB_MANIFEST);  

// Cache API responses with StaleWhileRevalidate  
registerRoute(  
  // Match API requests (adjust the URL pattern as needed)  
  ({ url }) => url.pathname.startsWith('/api/'),  
  new StaleWhileRevalidate({  
    cacheName: 'api-cache',  # Name for the cache storage  
  })  
);  

Step 3: Test with Lighthouse

Lighthouse is a tool built into Chrome DevTools that audits PWA compliance, performance, and accessibility. Use it to validate your PWA.

How to Run Lighthouse

  1. Build your React app for production:
    npm run build  
  2. Serve the build folder locally (use serve for simplicity):
    npx serve -s build  
  3. Open Chrome, navigate to http://localhost:3000, and open DevTools (F12).
  4. Go to the Lighthouse tab, check “Progressive Web App”, and click “Generate report”.

Lighthouse PWA Checklist

A passing PWA score requires:

  • A valid Web App Manifest.
  • A registered service worker with offline support.
  • HTTPS (or localhost for development).
  • Responsive design (mobile-friendly).
  • Fast load times (LCP < 2.5s, FID < 100ms).

Step 4: Add Offline Functionality

To test offline support:

  1. Run npm run build and npx serve -s build.
  2. In Chrome DevTools, go to Application > Service Workers, check “Offline”, and refresh the page.

Your app should load even without internet! For dynamic content (e.g., user data), use Workbox strategies like NetworkFirst to fetch fresh data when online and fall back to cached data when offline.

Step 5: Enable Installability

Users can install your PWA to their home screen if it meets these criteria:

  • A valid manifest.json with display: standalone.
  • A registered service worker.
  • Served over HTTPS (or localhost for development).

Triggering the Install Prompt

Browsers automatically show an “Install” prompt when criteria are met, but you can customize the timing (e.g., after the user interacts with your app).

Add this code to src/App.js to capture the install prompt:

// src/App.js  
import { useState, useEffect } from 'react';  

function App() {  
  const [installPrompt, setInstallPrompt] = useState(null);  

  useEffect(() => {  
    // Listen for the "beforeinstallprompt" event  
    const handleBeforeInstallPrompt = (e) => {  
      e.preventDefault(); // Prevent the browser from showing the default prompt  
      setInstallPrompt(e); // Store the event to trigger later  
    };  

    window.addEventListener('beforeinstallprompt', handleBeforeInstallPrompt);  

    return () => {  
      window.removeEventListener('beforeinstallprompt', handleBeforeInstallPrompt);  
    };  
  }, []);  

  const handleInstallClick = async () => {  
    if (!installPrompt) return;  

    // Show the install prompt  
    installPrompt.prompt();  
    const { outcome } = await installPrompt.userChoice;  

    if (outcome === 'accepted') {  
      console.log('User installed the app!');  
    }  
    setInstallPrompt(null); // Reset the prompt  
  };  

  return (  
    <div>  
      <h1>My React PWA</h1>  
      {installPrompt && (  
        <button onClick={handleInstallClick}>Install App</button>  
      )}  
    </div>  
  );  
}  

export default App;  

Advanced PWA Features

For more functionality, explore these APIs:

Background Sync

Delay API requests until the user is back online. Use workbox-background-sync:

// src/service-worker.js  
import { BackgroundSyncPlugin } from 'workbox-background-sync';  

registerRoute(  
  ({ url }) => url.pathname.startsWith('/api/submit-data'),  
  new NetworkFirst({  
    plugins: [  
      new BackgroundSyncPlugin('submit-data-queue', {  
        maxRetentionTime: 24 * 60, // Retry for 24 hours  
      }),  
    ],  
  }),  
  'POST' // Only for POST requests  
);  

Push Notifications

Send real-time updates with the Push API. Requires a backend server and user permission. See Google’s Push Notifications Guide.

Deployment

PWAs must be served over HTTPS (except localhost). Popular hosting options:

Netlify

  1. Push your code to GitHub.
  2. Connect your repo to Netlify.
  3. Build command: npm run build, publish directory: build.

Vercel

Similar to Netlify—connect your GitHub repo, and Vercel auto-deploys on push.

Firebase Hosting

  1. Install Firebase CLI: npm install -g firebase-tools.
  2. Run firebase init, select “Hosting”, and link your project.
  3. Deploy with firebase deploy.

Troubleshooting Common Issues

Service Worker Not Updating

Browsers cache service workers. To force an update:

  • Increment the version in service-worker.js (e.g., const CACHE_VERSION = 'v2').
  • Use workbox-precaching’s built-in cache busting (auto-enabled in CRA).

Manifest Icons Not Loading

Ensure icon paths in manifest.json are correct (e.g., src: "icons/icon-512x512.png"). Test paths with DevTools Application > Manifest.

Install Prompt Not Showing

Check Lighthouse for errors. Common fixes:

  • Add display: standalone to manifest.json.
  • Register the service worker in production.
  • Serve over HTTPS (use localhost for development).

Conclusion

Building a PWA with React is straightforward with create-react-app and Workbox. By following this guide, you’ve learned to:

  • Configure a Web App Manifest for branding and installability.
  • Use service workers for offline support.
  • Test with Lighthouse for PWA compliance.
  • Add install prompts and advanced features like background sync.

PWAs offer a cost-effective way to deliver native-like experiences across devices—start building yours today!

References