Blogs/Technology

How To Build Progressive Web Apps (PWAs) with React?

Written byRiswana Begam A
Aug 6, 2026
14 Min Read
How To Build Progressive Web Apps (PWAs) with React? Hero
Too Long? Read This First

- A Progressive Web App is a web application enhanced with installation, offline, and device-integration capabilities.
- React manages the interface but does not provide PWA functionality by itself.
- A web app manifest describes how the installed application should appear and launch.
- A service worker controls network requests and enables caching and offline behaviour.
- Create React App is deprecated, so this guide uses Vite and vite-plugin-pwa.
- PWAs must be served through HTTPS in production; service workers work on localhost during development.
- Do not cache every API response. Choose a strategy based on how fresh and sensitive the data is.
- Test installation, updates, offline routes, and cached data on real devices before launching.

Users expect web applications to load quickly, remain useful on unstable networks, and feel natural on whichever device they use. Meeting those expectations becomes difficult when a web app depends entirely on a reliable internet connection.

That is where Progressive Web Apps help. While building PWAs with React, I have found that creating the interface is usually the easy part. The real work lies in choosing what should function offline, configuring safe caching rules, handling application updates, and testing how the app behaves when the network disappears.

In this guide, we’ll build a React PWA using Vite, add an installable web app manifest, generate a service worker, configure offline caching, and prepare the application for production.

What Is a Progressive Web App?

A Progressive Web App is a web application that uses modern browser capabilities to provide an installable, reliable, and app-like experience.

Users access a PWA through a URL, just like a regular website. On supported browsers and devices, they can also install it, launch it from the home screen or application menu, and use the parts designed to work without a network connection.

A PWA remains a web application. It is built using HTML, CSS, and JavaScript and does not automatically gain every capability available to a native Android or iOS application.

What Role Does React Play in a PWA?

React is responsible for building and updating the user interface. Its component-based structure works well for applications with interactive screens, shared state, reusable layouts, and frequently changing data.

PWA behaviour comes from browser technologies outside React:

  • The web app manifest controls installation metadata.
  • The service worker handles network requests and caching.
  • Cache Storage stores responses and static assets.
  • IndexedDB can store structured offline data.
  • The Push API and Notifications API support notifications where available.

React and PWA technologies solve different parts of the application. React builds the interface, while the browser APIs make that interface more reliable and installable.

What Are the Core Features of a PWA?

Installability

Users can install the web application on a supported device without downloading a conventional application package from an app store.

Network Resilience

A service worker can serve cached content when the network is slow or unavailable. The level of offline support depends on how the application is designed.

Fast Repeat Visits

Important static assets can be stored locally, reducing the amount of data that must be downloaded when the user returns.

App-Like Display

The application can open in a standalone window without the full browser interface.

Progressive Enhancement

Users on browsers without complete PWA support can still access the application as a normal website.

Device Capabilities

Depending on browser and operating-system support, PWAs may use features such as notifications, background sync, file handling, sharing, shortcuts, and badges.

Before You Start

You need a current Node.js installation and a package manager such as npm.

Check your installation:

node --version
npm --version

The project in this guide uses:

  • React
  • Vite
  • vite-plugin-pwa
  • Workbox through the Vite PWA plugin

React officially deprecated Create React App for new applications in 2025 and recommends using a framework or a modern build tool such as Vite.

How to Build a React PWA in 7 Steps

Step 1: Create a React Application With Vite

Create the project:

npm create vite@latest my-react-pwa -- --template react
cd my-react-pwa
npm install

Start the development server:

npm run dev

Open the local URL shown in the terminal. The React application should now be running.

The initial project structure will resemble:

my-react-pwa/
├── public/
├── src/
│   ├── App.jsx
│   ├── main.jsx
│   └── index.css
├── index.html
├── package.json
└── vite.config.js

Step 2: Install the Vite PWA Plugin

Install vite-plugin-pwa as a development dependency:

npm install -D vite-plugin-pwa

The plugin can:

  • Generate the web app manifest
  • Generate a service worker
  • Precache production assets
  • Register the service worker
  • Configure runtime caching through Workbox
  • Detect when a new application version is available

Using a plugin reduces the amount of service-worker lifecycle and asset-versioning logic you need to maintain manually.

Step 3: Add the PWA Icons

Create the following icons and place them inside public/:

public/
├── favicon.ico
├── apple-touch-icon.png
├── pwa-192x192.png
├── pwa-512x512.png
└── maskable-icon-512x512.png

Recommended sizes are:

FileRecommended sizePurpose
pwa-192x192.png192 × 192Standard application icon
pwa-512x512.png512 × 512Large installation icon
maskable-icon-512x512.png512 × 512Icon designed for adaptive masks
apple-touch-icon.png180 × 180Apple home-screen icon
pwa-192x192.png
Recommended size
192 × 192
Purpose
Standard application icon
1 of 4

A maskable icon needs additional safe space around its main design. Otherwise, parts of the logo may be cropped when a device applies a circle, square, or rounded mask.

Do not create one low-resolution icon and resize it for every purpose. Check the icons on light and dark backgrounds and test how the maskable version appears in different shapes.

Step 4: Configure the Manifest and Service Worker

Open vite.config.js and replace its contents with:

import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import { VitePWA } from "vite-plugin-pwa";

export default defineConfig({
  plugins: [
    react(),
    VitePWA({
      registerType: "prompt",

      includeAssets: [
        "favicon.ico",
        "apple-touch-icon.png",
      ],

      manifest: {
        name: "My React Progressive Web App",
        short_name: "My React PWA",
        description: "A Progressive Web App built with React and Vite",
        theme_color: "#0f172a",
        background_color: "#ffffff",
        display: "standalone",
        scope: "/",
        start_url: "/",
        orientation: "portrait-primary",

        icons: [
          {
            src: "pwa-192x192.png",
            sizes: "192x192",
            type: "image/png",
          },
          {
            src: "pwa-512x512.png",
            sizes: "512x512",
            type: "image/png",
          },
          {
            src: "maskable-icon-512x512.png",
            sizes: "512x512",
            type: "image/png",
            purpose: "maskable",
          },
        ],
      },

      workbox: {
        globPatterns: [
          "**/*.{js,css,html,ico,png,svg,webp,woff2}",
        ],

        runtimeCaching: [
          {
            urlPattern: /\.(?:png|jpg|jpeg|svg|gif|webp)$/,
            handler: "CacheFirst",
            options: {
              cacheName: "image-cache",
              expiration: {
                maxEntries: 60,
                maxAgeSeconds: 30 * 24 * 60 * 60,
              },
              cacheableResponse: {
                statuses: [0, 200],
              },
            },
          },
        ],
      },
    }),
  ],
});

This configuration generates the manifest and service worker during the production build.

Understanding the Manifest

The important manifest properties are:

  • name: Full application name displayed during installation
  • short_name: Shorter name used when screen space is limited
  • description: Short explanation of the application
  • start_url: The page opened when the installed app launches
  • scope: URLs controlled as part of the installed application
  • display: Determines whether the app opens with browser controls
  • theme_color: Colour used by supported browser and operating-system interfaces
  • background_color: Colour displayed while the application loads
  • icons: Images used for installation, launchers, and operating-system surfaces

The manifest describes the installed experience. It does not create offline support by itself.

Understanding the Workbox Configuration

globPatterns tells Workbox which generated assets should be precached. Each production build includes versioned asset names, allowing changed files to be updated without unnecessarily downloading every asset again.

The runtime-caching rule stores images using CacheFirst. When the image is already cached, it is served locally. Otherwise, it is requested from the network and saved for later use.

The cache is limited to 60 images and expires entries after 30 days. These limits prevent the application from storing an unlimited number of files.

Step 5: Register the Service Worker and Handle Updates

Create a file named src/registerSW.js:

import { registerSW } from "virtual:pwa-register";

const updateSW = registerSW({
  onNeedRefresh() {
    const shouldUpdate = window.confirm(
      "A new version is available. Update now?"
    );

    if (shouldUpdate) {
      updateSW(true);
    }
  },

  onOfflineReady() {
    console.log("The application is ready to work offline.");
  },

  onRegisteredSW(serviceWorkerUrl, registration) {
    console.log(
      "Service worker registered:",
      serviceWorkerUrl,
      registration
    );
  },

  onRegisterError(error) {
    console.error(
      "Service worker registration failed:",
      error
    );
  },
});

Import it near the top of src/main.jsx:

import "./registerSW";

A complete main.jsx may look like:

import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import "./index.css";
import "./registerSW";
import App from "./App.jsx";

createRoot(document.getElementById("root")).render(
  <StrictMode>
    <App />
  </StrictMode>
);

The update prompt prevents the application from silently replacing the service worker while a user is completing a task.

Let’s Build Your Web App Together!

We build fast, scalable, and secure web applications that help your business grow. From idea to launch, we handle it all.

For a content-only application, automatic updates may be acceptable. For dashboards, forms, checkout flows, or tools containing unsaved work, prompting the user is generally safer.

Step 6: Design the Offline Experience

A PWA should not claim to work offline unless its important user journeys have been deliberately designed for it.

There are several possible levels of offline support:

Offline Application Shell

The main interface loads, but live data may be unavailable. Navigation, branding, and previously cached static content remain visible.

Previously Viewed Content

Pages or data the user has already accessed can be shown from a local cache.

Offline Read and Write

Users can create or edit information while disconnected. The application stores the changes locally and synchronises them later.

Complete Offline Operation

Most important functionality remains available without a network. This requires careful local storage, conflict resolution, and synchronisation design.

Not every application needs complete offline operation. A payment dashboard, for example, may be better served by displaying a clear offline status than by showing outdated financial information.

Displaying Network Status in React

Create src/NetworkStatus.jsx:

import { useEffect, useState } from "react";

function NetworkStatus() {
  const [isOnline, setIsOnline] = useState(
    navigator.onLine
  );

  useEffect(() => {
    const handleOnline = () => setIsOnline(true);
    const handleOffline = () => setIsOnline(false);

    window.addEventListener("online", handleOnline);
    window.addEventListener("offline", handleOffline);

    return () => {
      window.removeEventListener(
        "online",
        handleOnline
      );

      window.removeEventListener(
        "offline",
        handleOffline
      );
    };
  }, []);

  if (isOnline) {
    return null;
  }

  return (
    <div role="status" className="offline-banner">
      You are offline. Some information may be unavailable.
    </div>
  );
}

export default NetworkStatus;

Use it inside App.jsx:

import NetworkStatus from "./NetworkStatus";

function App() {
  return (
    <>
      <NetworkStatus />

      <main>
        <h1>My React PWA</h1>
        <p>
          This application can load its cached interface
          when the network is unavailable.
        </p>
      </main>
    </>
  );
}

export default App;

navigator.onLine provides a useful signal, but it does not prove that your API is reachable. The device may be connected to a network that has no internet access, or the API itself may be unavailable.

Treat it as a user-interface hint, not a complete connectivity check.

Step 7: Build, Test, and Deploy the PWA

Create a production build:

npm run build

Vite places the output inside dist/.

Preview it locally:

npm run preview

Testing the production build is important because the generated service worker and manifest may not behave the same way during the normal development server session.

What to Test

Check the following:

  • The manifest loads without errors.
  • The service worker installs and activates.
  • The application can be installed on supported devices.
  • Icons display correctly after installation.
  • The app launches through the configured start_url.
  • Cached pages remain usable offline.
  • Uncached pages fail gracefully.
  • A new deployment triggers the expected update experience.
  • Old cached assets are removed eventually.
  • Forms do not lose data during an update.
  • Authenticated or sensitive responses are not cached accidentally.

Testing With Browser Developer Tools

In Chromium-based browser developer tools:

  1. Open the Application panel.
  2. Check Manifest for missing fields or icon problems.
  3. Open Service Workers to inspect the registration.
  4. Open Cache Storage to see cached responses.
  5. Enable Offline mode under the Network panel.
  6. Refresh the application and test important routes.

Use Lighthouse for performance, accessibility, and best-practice audits. PWA behaviour should also be tested directly through the Application panel and on real devices.

Deployment Requirements

Production service workers require a secure context. Deploy the application through HTTPS using a platform such as:

  • Vercel
  • Netlify
  • Firebase Hosting
  • Cloudflare Pages
  • AWS
  • A properly configured web server

localhost is treated as a secure development environment, but an ordinary production HTTP URL is not.

For a single-page React application, configure the hosting platform to return index.html for client-side routes. Without this fallback, opening an installed PWA directly on a nested route may return a server-side 404 error.

Choosing the Right Caching Strategy

Caching every request with the same strategy is one of the quickest ways to create stale or incorrect behaviour.

Workbox provides several common strategies.

Cache First

The service worker checks the cache before using the network.

Best suited for:

  • Versioned images
  • Fonts
  • Icons
  • Static media that rarely changes

Risk: Users may continue seeing an old resource until the entry expires or the cache is refreshed.

Network First

The service worker requests the latest response and falls back to the cache if the network fails.

Best suited for:

  • Frequently updated public content
  • Article lists
  • Product listings
  • Data that should be fresh but can be slightly outdated offline

Risk: A slow network can delay the response before the cached version is used.

Stale While Revalidate

The cached response is returned immediately while the service worker requests an updated version in the background.

Best suited for:

  • Avatars
  • News feeds
  • Content thumbnails
  • Non-critical API responses

Risk: The user may briefly see outdated information.

Network Only

The request always goes to the network.

Best suited for:

  • Payments
  • Authentication
  • Sensitive account operations
  • Real-time financial information
  • Requests that must never use stale data

Cache Only

The request is served only when it already exists in the cache.

Best suited for:

  • Precached application assets
  • Fully controlled offline resources

The correct strategy depends on how damaging stale data would be. A cached logo can be old without causing harm. A cached account balance or checkout response can mislead the user.

Should You Cache API Responses?

API caching requires more caution than caching images, scripts, and styles.

Before caching an API response, ask:

  • Does the response contain personal or sensitive data?
  • Can it be safely viewed after the user signs out?
  • How quickly does the information become outdated?
  • Could stale data cause a wrong user action?
  • Is the response shared between users?
  • Does the request contain an authorisation header?
  • What should happen when the server and offline data disagree?

Avoid broad rules that cache every /api/ request. Define specific patterns for public, read-only endpoints and keep sensitive operations network-only.

When an application supports offline writes, store pending operations in IndexedDB and build an explicit synchronisation process. Do not assume that caching a POST request provides reliable offline mutation support.

Adding Push Notifications

Push notifications can re-engage users, but they are optional and should not be presented as a requirement for every PWA.

A complete web-push implementation requires:

  1. A registered service worker
  2. Notification permission from the user
  3. A push subscription
  4. A backend that stores subscriptions
  5. A push service and application-server keys
  6. Service-worker handlers for push and notification-click events
  7. Unsubscribe and preference management

Do not request notification permission immediately when the application loads. Ask after the user performs a relevant action and clearly explain what notifications they will receive.

For example, an order-tracking application could ask:

Would you like to receive a notification when your order is dispatched?

That gives the user context and a reason to make the decision.

Browser and operating-system support varies, so the application should remain useful when notifications are unavailable or declined.

Common Challenges When Building React PWAs

Stale Application Versions

A previously installed service worker may continue controlling open tabs while a new version waits to activate.

Solution: Provide an update prompt, test the service-worker lifecycle, and avoid forcing reloads while users have unsaved data.

Incorrect Caching Rules

A broad caching rule may store private, outdated, or error responses.

Solution: Cache only clearly identified resources, limit cache size and age, and keep sensitive operations network-only.

Offline Routes Failing

The homepage may work offline while nested routes fail.

Solution: Test every important route, configure a navigation fallback, and ensure the hosting platform supports single-page application routing.

Offline Data Conflicts

A user may edit the same record offline while another device updates it online.

Solution: Define a conflict policy using timestamps, version numbers, server authority, merging, or explicit user resolution.

Let’s Build Your Web App Together!

We build fast, scalable, and secure web applications that help your business grow. From idea to launch, we handle it all.

Browser Differences

Installation, notifications, background features, and update behaviour vary between browsers and operating systems.

Solution: Use feature detection, provide fallbacks, and test the application on actual target devices.

Storage Limits

Browsers can remove cached data under storage pressure, and available storage varies by device.

Solution: Keep caches bounded, avoid depending on them as the only copy of important data, and rebuild cached content when necessary.

Difficult Service-Worker Debugging

An old service worker can continue affecting the application after code changes.

Solution: Inspect registrations and cache storage in browser developer tools. During development, unregister old workers and clear site data when testing a new caching configuration.

Best Practices for React PWAs

Keep the Initial Bundle Small

Use route-level code splitting and load features only when they are needed:

import { lazy, Suspense } from "react";

const Reports = lazy(() => import("./Reports"));

function App() {
  return (
    <Suspense fallback={<p>Loading reports...</p>}>
      <Reports />
    </Suspense>
  );
}

Optimise Images

Use modern formats such as WebP or AVIF, responsive image sizes, appropriate compression, and lazy loading for content outside the initial viewport.

Provide a Clear Offline State

Tell users when information may be unavailable or outdated. Do not allow a failed network request to leave the interface loading indefinitely.

Preserve Unsaved Work

Store drafts locally where appropriate and avoid applying updates in the middle of important forms or transactions.

Respect Accessibility

An installable application must still support keyboard navigation, readable contrast, labelled controls, logical focus movement, and assistive technologies.

Protect Cached Data

Do not cache confidential responses unless the threat model and sign-out behaviour have been carefully designed.

Measure Real Performance

Track Core Web Vitals, loading time, JavaScript errors, service-worker failures, and API latency on real user devices.

Test Upgrades

A PWA must work not only on its first installation but also after repeated deployments, schema changes, cache changes, and service-worker updates.

Benefits of Building a PWA With React

One Web Codebase

A React PWA can support desktop and mobile browsers without maintaining separate native codebases for every platform.

Easier Distribution

Users can access the application through a URL and install it where supported. Updates can be delivered through the web rather than waiting for every user to download a new application package.

Better Performance on Repeat Visits

Precached assets and suitable runtime caching can reduce repeat downloads and make important screens feel faster.

Improved Network Resilience

A carefully designed offline experience allows users to access cached content or continue selected tasks during temporary network failures.

Wider Reach

A PWA remains accessible as a website, even for users who do not install it or whose browsers provide limited PWA features.

Lower Development Overhead

A PWA can reduce the need for separate platform-specific applications when the product does not depend heavily on native operating-system capabilities.

Teams building a complex PWA may still need help with React architecture, caching, offline synchronisation, testing, and performance. Experienced React JS developers can help design these features around the application’s real user journeys instead of adding offline behaviour as an afterthought.

When Should You Choose a PWA?

A PWA can be a strong choice for:

  • E-commerce storefronts
  • Content and publishing platforms
  • Booking applications
  • Internal business tools
  • Field-service applications
  • Learning platforms
  • Customer portals
  • Event applications
  • Products targeting users on inconsistent networks

A native application may be more suitable when the product requires:

  • Extensive background processing
  • Deep operating-system integration
  • Advanced Bluetooth or hardware access
  • Highly specialised native interfaces
  • Platform-specific monetisation
  • Features unsupported by target browsers

The right decision depends on the required capabilities, audience, distribution strategy, and development resources.

Frequently Asked Questions

Is React Suitable for Production PWAs?

Yes. React works well for PWA interfaces when combined with a manifest, service worker, appropriate caching strategies, secure deployment, offline planning, update handling, monitoring, and cross-browser testing.

Does React Provide Offline Support?

No. React manages the user interface. Offline support comes from service workers, Cache Storage, IndexedDB, and application-specific synchronisation logic. React components can then communicate the resulting network and data state.

No. React deprecated Create React App for new projects in 2025. New client-side React applications can use a modern build tool such as Vite or an appropriate React framework.

Can a React PWA Work Completely Offline?

Yes, but only when its assets, routes, data, and user actions are deliberately designed for offline use. Complete offline support is considerably more complex than caching the initial application interface.

Are React PWAs SEO-Friendly?

They can be. Search visibility depends on crawlable content, rendering strategy, metadata, internal linking, performance, and indexability. Installing a service worker does not automatically improve SEO.

Do PWAs Work on iPhones?

PWAs can be installed and used on iPhones, but available capabilities and installation behaviour differ from Android and desktop platforms. Test the required features on the specific iOS versions and browsers you support.

Can a PWA Send Push Notifications?

Yes, on supported browsers and operating systems. The application needs permission, a push subscription, backend delivery logic, and service-worker event handlers. Users should be given a clear reason before permission is requested.

Does a PWA Need HTTPS?

Yes, service workers and most PWA capabilities require a secure context in production. Browsers make an exception for localhost so developers can build and test locally without an HTTPS certificate.

Can a PWA Be Published in an App Store?

In some cases, yes. PWAs can be packaged or submitted through supported store mechanisms, but store availability, requirements, and supported capabilities vary between platforms.

Our Final Words

Building a React PWA is not simply a matter of generating a manifest and turning on a service worker. Those steps make installation and caching possible, but they do not decide what the application should do when data is unavailable, an update arrives, or an offline edit conflicts with the server.

What matters most is identifying the user journeys that genuinely benefit from installation and network resilience. Cache stable assets, handle sensitive data carefully, communicate offline states clearly, and test updates as thoroughly as the initial installation.

Start with a reliable application shell and one useful offline experience. Once that works consistently across your target devices, expand the caching and offline capabilities based on real user needs.

Author-Riswana Begam A
Riswana Begam A

I’m a tech returnee with a passion for coding, and I stay up-to-date with the latest industry trends. I have a total of 7 years of experience, with 3 years specifically in the tech field.

Share this article

Phone

Next for you

8 Best GraphQL Libraries for Node.js in 2025 Cover

Technology

Aug 4, 202613 min read

8 Best GraphQL Libraries for Node.js in 2025

8 Best GraphQL Libraries for Node.js in 2026 Too Long? Read This First - Choose Apollo Server when you need a mature ecosystem, GraphOS integration, plugins, or Apollo Federation. - Choose GraphQL Yoga for a modern, portable server with Fetch API compatibility and built-in support for subscriptions over Server-Sent Events. - Choose Mercurius when your application already uses Fastify and runtime efficiency is a major priority. - Use GraphQL.js when you need the official JavaScript implementati

9 React Native Animation Libraries and Tools Compared Cover

Technology

Aug 4, 202615 min read

9 React Native Animation Libraries and Tools Compared

Too Long? Read This First - Use React Native Reanimated for gesture-driven, interruptible, and performance-sensitive interface animations. - Use the built-in Animated API for simple fades, transforms, and timed sequences without another dependency. - Pair React Native Gesture Handler with Reanimated for swipes, dragging, pinching, rotation, and other touch-driven experiences. - Use Lottie React Native for non-interactive motion graphics supplied by designers. - Choose React Native Skia for cust

9 Critical Practices for Secure Web Application Development Cover

Technology

Aug 4, 202616 min read

9 Critical Practices for Secure Web Application Development

Too Long? Read This First - Define security requirements and model threats before implementation begins. - Treat authentication, account recovery, and MFA as one complete identity system. - Apply server-side authorization to every protected action and object. - Prevent injection with parameterized APIs, structured validation, safe output handling, and restricted outbound requests. - Protect sessions and tokens throughout their complete lifecycle. - Minimise sensitive data and manage encryption