How to use Vue with Service Workers
Service workers enable Vue applications to work offline, cache assets, and provide PWA features like push notifications. As the creator of CoreUI with 12 years of Vue development experience, I’ve built Vue PWAs that serve millions of users with reliable offline functionality.
The most maintainable approach is to use Vite PWA plugin with Workbox for automatic service worker generation and Vue integration.
Install Dependencies
Install Vite PWA plugin:
npm install vite-plugin-pwa -D
Configure Vite
Update vite.config.js:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { VitePWA } from 'vite-plugin-pwa'
export default defineConfig({
plugins: [
vue(),
VitePWA({
registerType: 'autoUpdate',
includeAssets: ['favicon.ico', 'robots.txt', 'apple-touch-icon.png'],
manifest: {
name: 'My Vue App',
short_name: 'VueApp',
description: 'My awesome Vue application',
theme_color: '#ffffff',
icons: [
{
src: 'pwa-192x192.png',
sizes: '192x192',
type: 'image/png'
},
{
src: 'pwa-512x512.png',
sizes: '512x512',
type: 'image/png'
}
]
},
workbox: {
runtimeCaching: [
{
urlPattern: /^https:\/\/api\.example\.com\/.*/i,
handler: 'NetworkFirst',
options: {
cacheName: 'api-cache',
expiration: {
maxEntries: 50,
maxAgeSeconds: 60 * 60 * 24
},
cacheableResponse: {
statuses: [0, 200]
}
}
}
]
}
})
]
})
Register Service Worker
Update src/main.js:
import { createApp } from 'vue'
import App from './App.vue'
import { registerSW } from 'virtual:pwa-register'
const app = createApp(App)
const updateSW = registerSW({
onNeedRefresh() {
console.log('New content available, please refresh')
},
onOfflineReady() {
console.log('App ready to work offline')
},
onRegistered(registration) {
console.log('Service worker registered:', registration)
},
onRegisterError(error) {
console.error('Service worker registration error:', error)
}
})
app.mount('#app')
Create Service Worker Status Component
Create src/components/ServiceWorkerStatus.vue:
<script setup>
import { ref, onMounted } from 'vue'
import { useRegisterSW } from 'virtual:pwa-register/vue'
const {
needRefresh,
updateServiceWorker
} = useRegisterSW()
const close = () => {
needRefresh.value = false
}
const update = async () => {
await updateServiceWorker()
}
</script>
<template>
<div v-if="needRefresh" class="pwa-toast">
<div class="message">
New content available. Click reload to update.
</div>
<button @click="update">Reload</button>
<button @click="close">Close</button>
</div>
</template>
<style scoped>
.pwa-toast {
position: fixed;
bottom: 20px;
right: 20px;
padding: 16px;
background: #333;
color: white;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
z-index: 1000;
}
.message {
margin-bottom: 12px;
}
button {
margin-right: 8px;
padding: 8px 16px;
border: none;
border-radius: 4px;
cursor: pointer;
}
</style>
Create Offline Detection Composable
Create src/composables/useOnline.js:
import { ref, onMounted, onUnmounted } from 'vue'
export const useOnline = () => {
const isOnline = ref(navigator.onLine)
const updateOnlineStatus = () => {
isOnline.value = navigator.onLine
}
onMounted(() => {
window.addEventListener('online', updateOnlineStatus)
window.addEventListener('offline', updateOnlineStatus)
})
onUnmounted(() => {
window.removeEventListener('online', updateOnlineStatus)
window.removeEventListener('offline', updateOnlineStatus)
})
return { isOnline }
}
Use in App
<script setup>
import { useOnline } from './composables/useOnline'
import ServiceWorkerStatus from './components/ServiceWorkerStatus.vue'
const { isOnline } = useOnline()
</script>
<template>
<div id="app">
<div v-if="!isOnline" class="offline-banner">
You are offline. Some features may be unavailable.
</div>
<ServiceWorkerStatus />
<router-view />
</div>
</template>
<style>
.offline-banner {
position: fixed;
top: 0;
left: 0;
right: 0;
background: #f44336;
color: white;
padding: 12px;
text-align: center;
z-index: 999;
}
</style>
Custom Caching Strategy
For advanced caching, create public/sw.js:
import { precacheAndRoute } from 'workbox-precaching'
import { registerRoute } from 'workbox-routing'
import { StaleWhileRevalidate, CacheFirst, NetworkFirst } from 'workbox-strategies'
import { ExpirationPlugin } from 'workbox-expiration'
precacheAndRoute(self.__WB_MANIFEST)
// Cache images
registerRoute(
({ request }) => request.destination === 'image',
new CacheFirst({
cacheName: 'images',
plugins: [
new ExpirationPlugin({
maxEntries: 60,
maxAgeSeconds: 30 * 24 * 60 * 60
})
]
})
)
// Cache API calls
registerRoute(
({ url }) => url.pathname.startsWith('/api/'),
new NetworkFirst({
cacheName: 'api',
plugins: [
new ExpirationPlugin({
maxEntries: 50,
maxAgeSeconds: 5 * 60
})
]
})
)
Best Practice Note
This is the same service worker integration we use in CoreUI’s Vue admin templates. Vite PWA plugin automatically generates optimized service workers with Workbox, providing offline support and asset caching without manual configuration.
For production applications, consider using CoreUI’s Vue Admin Template which includes pre-configured PWA support with service workers, offline caching, and push notifications.
Related Articles
If you’re building offline features, you might also want to learn how to use Vue with IndexedDB for local data storage.



