<header class="flex items-center">

<NuxtImg src="logo.png" class="w-18" alt="Dywan Dev - Digital tools & templates" format="webp" quality="80" loading="lazy" />

<Item><nuxt-link href="/">Home</nuxt-link></Item>

<Item><nuxt-link href="/about">About</nuxt-link></Item>

<Item><nuxt-link href="/works"> Portfolio</nuxt-link></Item>

<Item><nuxt-link href="/contact_me"> Contact</nuxt-link></Item>

<a href="/contact-me" title="Get in touch with Dywan Dev"> Contact </a>

<themeswitch class="moon">Dark/Light</themeswitch>

<langswitch class="en">en/fa</langswitch>

</header>

<initilizecontent class="content">Hello World - Dywan Dev</initilizecontent>

<myname is="Dywan Dev" />

<jobtitle is="Digital tools · Templates · Case studies" />

<portfolios are="ready" number="8" />

<experience years="more than 6" />

<birthdate year="1998" month="july" day="11" />

<skills :list="['NUXT', 'Vue', 'React', 'Next', 'Javascript','Responsive Design', 'i18n', 'TypeScript]" />

<contactDetails :list="['contact@dywandev.com', 'linkedin.com/in/dywan-dev', 'github.com/dywan-dev']" />

Dywan Dev
Blog
How to Add PWA Support to a React Vite App in 2026
5m

How to Add PWA Support to a React Vite App in 2026

Introduction

Most websites load. A PWA survives.

When a user loses their internet connection, a regular React app shows a blank screen or an error. A Progressive Web App keeps working — serving cached pages, preserving the experience, and syncing when the connection returns.

Adding PWA support to a React Vite app is one of the highest-value improvements you can make for performance, user retention, and Lighthouse scores. This guide shows you exactly how I implemented it in Dywan Dev and Savoura Dywan — including the install prompt, offline page, and update notification that most tutorials skip.

What Is a PWA and Why Does It Matter

A Progressive Web App is a web application that behaves like a native app. It can be installed on a user's device, works offline, loads instantly on repeat visits, and sends push notifications.

For businesses, this means lower bounce rates and higher engagement. For developers, it means better Lighthouse scores and a stronger product offering. For you as a freelancer or template seller — it's a feature clients will pay more for.

The three core requirements of a PWA are a web manifest, a service worker, and HTTPS. Vite makes all three straightforward with one plugin.

1. Install the Vite PWA Plugin

npm install -D vite-plugin-pwa

This plugin handles service worker generation and manifest injection automatically. It uses Workbox under the hood — the same library Google uses for PWA tooling.

2. Configure the Plugin in vite.config.js

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

export default defineConfig({
  plugins: [
    react(),
    VitePWA({
      registerType: 'autoUpdate',
      includeAssets: ['favicon.ico', 'apple-touch-icon.png', 'mask-icon.svg'],
      manifest: {
        name: 'Dywan Dev',
        short_name: 'DywanDev',
        description: 'Modern web templates and fullstack solutions',
        theme_color: '#0f172a',
        background_color: '#0f172a',
        display: 'standalone',
        scope: '/',
        start_url: '/',
        icons: [
          {
            src: 'pwa-192x192.png',
            sizes: '192x192',
            type: 'image/png'
          },
          {
            src: 'pwa-512x512.png',
            sizes: '512x512',
            type: 'image/png'
          },
          {
            src: 'pwa-512x512.png',
            sizes: '512x512',
            type: 'image/png',
            purpose: 'any maskable'
          }
        ]
      },
      workbox: {
        globPatterns: ['**/*.{js,css,html,ico,png,svg,woff2}'],
        runtimeCaching: [
          {
            urlPattern: /^https:\/\/fonts\.googleapis\.com\/.*/i,
            handler: 'CacheFirst',
            options: {
              cacheName: 'google-fonts-cache',
              expiration: {
                maxEntries: 10,
                maxAgeSeconds: 60 * 60 * 24 * 365
              }
            }
          }
        ]
      }
    })
  ]
});

Replace the name, description, and theme color with your own branding. The icons need to exist in your public/ folder — generate them from your logo using a tool like PWA Asset Generator.

3. Create an Offline Fallback Page

This is what separates a real PWA from a basic service worker setup. When a user has no internet, show them something useful instead of a browser error.

Create public/offline.html:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
  <title>You're Offline</title>
  <style>
    body {
      font-family: sans-serif;
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: center;
      height: 100vh;
      margin: 0;
      background: #0f172a;
      color: #f8fafc;
      text-align: center;
      padding: 2rem;
    }
    h1 { font-size: 2rem; margin-bottom: 1rem; }
    p { color: #94a3b8; max-width: 400px; }
  </style>
</head>
<body>
  <h1>You're offline</h1>
  <p>It looks like you lost your connection. The page you're looking for will be available once you're back online.</p>
</body>
</html>

Then reference it in your Workbox config inside vite.config.js (add navigateFallback alongside your existing globPatterns):

workbox: {
  navigateFallback: '/offline.html',
  globPatterns: ['**/*.{js,css,html,ico,png,svg,woff2}']
}

4. Add an Install Prompt

Browsers show a native install prompt automatically — but you can intercept it and show your own UI at the right moment. This dramatically increases install rates.

Create src/hooks/usePWAInstall.js:

import { useState, useEffect } from 'react';

export const usePWAInstall = () => {
  const [installPrompt, setInstallPrompt] = useState(null);
  const [isInstallable, setIsInstallable] = useState(false);

  useEffect(() => {
    const handler = (e) => {
      e.preventDefault();
      setInstallPrompt(e);
      setIsInstallable(true);
    };

    window.addEventListener('beforeinstallprompt', handler);
    return () => window.removeEventListener('beforeinstallprompt', handler);
  }, []);

  const install = async () => {
    if (!installPrompt) return;
    await installPrompt.prompt();
    const { outcome } = await installPrompt.userChoice;
    if (outcome === 'accepted') setIsInstallable(false);
    setInstallPrompt(null);
  };

  return { isInstallable, install };
};

Use it in any component:

import { usePWAInstall } from './hooks/usePWAInstall';

export const InstallButton = () => {
  const { isInstallable, install } = usePWAInstall();

  if (!isInstallable) return null;

  return (
    <button onClick={install} className="install-btn">
      Install App
    </button>
  );
};

Place this button in your navbar or hero section. It only appears when the browser determines the app is installable — so it never shows unnecessarily.

5. Add an Update Notification

When you deploy a new version, users with the old service worker cached won't see your changes automatically. Show them a notification so they can refresh and get the latest version.

Update src/main.jsx:

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

const updateSW = registerSW({
  onNeedRefresh() {
    const confirmed = window.confirm(
      'A new version is available. Reload to update?'
    );
    if (confirmed) updateSW(true);
  },
  onOfflineReady() {
    console.log('App is ready to work offline');
  }
});

For a more polished experience, replace the window.confirm with a custom toast notification using your existing UI components.

6. Verify It Works

Run your build and preview it locally:

npm run build
npm run preview

Then open Chrome DevTools → Application → Service Workers. You should see your service worker registered and active. Check the Manifest tab to verify your icons and metadata are loading correctly.

Run a Lighthouse audit. A properly configured PWA should score in the green across Performance, Accessibility, and PWA categories.

Conclusion

PWA support is one of those features that costs a few hours to implement and pays back continuously — in Lighthouse scores, user retention, and client perception. When you tell a client their website works offline and can be installed on a phone like a native app, that's a conversation closer.

Every template I build at Dywan Dev ships with PWA support by default. It's not an extra — it's a standard.

Building a restaurant, portfolio, or business website? Dywan Dev templates come PWA-ready, multilingual, and optimized out of the box. Explore the templates.

Continue Reading

View allView all
WhatsApp