Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Nov 4, 2025

Implements VitePWA's prompt-based service worker update flow with user notifications, replacing the existing autoUpdate mode. Users now see toast notifications for offline-ready status and available updates, with explicit control over when to reload.

Changes

  • VitePWA Configuration - Changed registerType from 'autoUpdate' to 'prompt' in vite.config.ts

  • Type Declarations - Added vite-env.d.ts and updated tsconfig.app.json with vite-plugin-pwa/react types for proper TypeScript support

  • ReloadPrompt Component - New component using useRegisterSW hook from virtual:pwa-register/react

    • Toast notifications for offline-ready and update-available states
    • Hourly automatic service worker update checks
    • User-controlled reload and dismiss actions
  • App Integration - Mounted ReloadPrompt component outside router in App.tsx for global visibility

Implementation

const UPDATE_CHECK_INTERVAL_MS = 60 * 60 * 1000

function ReloadPrompt() {
  const {
    offlineReady: [offlineReady, setOfflineReady],
    needRefresh: [needRefresh, setNeedRefresh],
    updateServiceWorker,
  } = useRegisterSW({
    onRegistered(r) {
      if (r) {
        setInterval(() => r.update(), UPDATE_CHECK_INTERVAL_MS)
      }
    },
  })
  // ...
}

Screenshot

PWA toast notification

Toast notification in bottom-right corner showing "App ready to work offline" message with Close button.

Original prompt

Integrate VitePWA in the web platform. Take a look at the ReloadPrompt from the documentation:

React

You can use the built-in Vite virtual module virtual:pwa-register/react for React which will return useState stateful values (useState) for offlineReady and needRefresh.

WARNING

You will need to add workbox-window as a dev dependency to your Vite project.
Type declarations

TIP

If your TypeScript build step or IDE complain about not being able to find modules or type definitions on imports, add the following to the compilerOptions.types array of your tsconfig.json:

{
"compilerOptions": {
"types": [
"vite-plugin-pwa/client"
]
}
}

Or you can add the following reference in any of your d.ts files (for example, in vite-env.d.ts or global.d.ts):

///

From version 0.14.5 you can also use types definition for react instead of vite-plugin-pwa/client, you can use:

{
"compilerOptions": {
"types": [
"vite-plugin-pwa/react"
]
}
}

Or you can add the following reference in any of your d.ts files (for example, in vite-env.d.ts or global.d.ts):

///

declare module 'virtual:pwa-register/react' {
import type { Dispatch, SetStateAction } from 'react'
import type { RegisterSWOptions } from 'vite-plugin-pwa/types'

export type { RegisterSWOptions }

export function useRegisterSW(options?: RegisterSWOptions): {
needRefresh: [boolean, Dispatch<SetStateAction>]
offlineReady: [boolean, Dispatch<SetStateAction>]
updateServiceWorker: (reloadPage?: boolean) => Promise
}
}

Prompt for update

WARNING

The options provided to hooks are not reactive. Therefore, the callback references will be the first rendered options instead of the latest hook’s options. If you are doing complex logic with state changes, you will need to provide a stable reference function.

You can use this ReloadPrompt.tsx component:

import React from 'react'
import './ReloadPrompt.css'

import { useRegisterSW } from 'virtual:pwa-register/react'

function ReloadPrompt() {
const {
offlineReady: [offlineReady, setOfflineReady],
needRefresh: [needRefresh, setNeedRefresh],
updateServiceWorker,
} = useRegisterSW({
onRegistered(r) {
// eslint-disable-next-line prefer-template
console.log('SW Registered: ' + r)
},
onRegisterError(error) {
console.log('SW registration error', error)
},
})

const close = () => {
setOfflineReady(false)
setNeedRefresh(false)
}

return (


{ (offlineReady || needRefresh)
&&


{ offlineReady
? App ready to work offline
: New content available, click on reload button to update.
}

{ needRefresh && <button className="ReloadPrompt-toast-button" onClick={() => updateServiceWorker(true)}>Reload }
<button className="ReloadPrompt-toast-button" onClick={() => close()}>Close

}

)
}

export default ReloadPrompt

and its corresponding ReloadPrompt.css styles file:

.ReloadPrompt-container {
padding: 0;
margin: 0;
width: 0;
height: 0;
}
.ReloadPrompt-toast {
position: fixed;
right: 0;
bottom: 0;
margin: 16px;
padding: 12px;
border: 1px solid #8885;
border-radius: 4px;
z-index: 1;
text-align: left;
box-shadow: 3px 4px 5px 0 #8885;
background-color: white;
}
.ReloadPrompt-toast-message {
margin-bottom: 8px;
}
.ReloadPrompt-toast-button {
border: 1px solid #8885;
outline: none;
margin-right: 5px;
border-radius: 2px;
padding: 3px 10px;
}

Periodic SW Updates

As explained in Periodic Service Worker Updates, you can use this code to configure this behavior on your application with the virtual module virtual:pwa-register/react:

import { useRegisterSW } from 'virtual:pwa-register/react'

const intervalMS = 60 * 60 * 1000

const updateServiceWorker = useRegisterSW({
onRegistered(r) {
r && setInterval(() => {
r.update()
}, intervalMS)
}
})

The interval must be in milliseconds, in the example above it is configured to check the service worker every hour.

WARNING

This only applies when importing any of the virtual modules or using workbox-window module.

Since workbox-window uses a time-based heuristic algorithm to handle service worker updates, if you build your service worker and register it again, if the time between last registration and the new one is less than 1 minute, then, workbox-window will handle the service worker update found event as an external event, and so the behavior could be strange (for example, if using prompt, instead showing the dialog for new content available, the read...


💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

@vercel
Copy link

vercel bot commented Nov 4, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
learningmap Ready Ready Preview Comment Nov 4, 2025 7:47pm

Co-authored-by: mikebarkmin <2592379+mikebarkmin@users.noreply.github.com>
…ls, extract magic number

Co-authored-by: mikebarkmin <2592379+mikebarkmin@users.noreply.github.com>
Copilot AI changed the title [WIP] Integrate VitePWA into web platform Integrate VitePWA prompt mode with ReloadPrompt component Nov 4, 2025
Copilot AI requested a review from mikebarkmin November 4, 2025 19:04
@mikebarkmin mikebarkmin marked this pull request as ready for review November 4, 2025 19:49
@mikebarkmin mikebarkmin merged commit 9861e63 into main Nov 4, 2025
3 checks passed
@mikebarkmin mikebarkmin deleted the copilot/integrate-vitepwa branch November 4, 2025 19:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants