A comprehensive guide for implementing Service Workers in React applications, enabling offline support and PWA capabilities.
A Service Worker is a script (written in JavaScript) that runs in the background, separate from the main browser thread. It gives your web app powerful features like:
- Offline support
- Caching assets and API responses
- Background sync
- Push notifications
Think of it as a programmable network proxy between your app and the internet.
-
Registration
- The browser needs to register the service worker file
- Usually done in your main
index.jsormain.tsx
-
Installation
- First time it's downloaded, the browser runs the install event
- You can pre-cache files here
-
Activation
- Next, it runs the activate event
- Clean up old caches during this phase
-
Fetch Interception
- Once active, the service worker can intercept every network request
- Serve cached assets via the fetch event
- ✅ Complete offline support
- 🚀 Performance optimization with caching strategies
- 🔄 Automatic asset and API caching
- 📱 PWA-ready configuration
- 🌐 Fallback for offline network requests
# Create a new React app
npx create-react-app my-app
cd my-appIn src/index.js, replace:
serviceWorkerRegistration.unregister();with:
import * as serviceWorkerRegistration from './serviceWorkerRegistration';
serviceWorkerRegistration.register();Create public/custom-sw.js:
const CACHE_NAME = 'my-react-app-v1';
const urlsToCache = ['/', '/index.html', '/favicon.ico'];
// Install event
self.addEventListener('install', (event) => {
event.waitUntil(
caches.open(CACHE_NAME).then((cache) => cache.addAll(urlsToCache))
);
});
// Activate event
self.addEventListener('activate', (event) => {
event.waitUntil(
caches.keys().then((keys) =>
Promise.all(
keys.map((key) => {
if (key !== CACHE_NAME) {
return caches.delete(key);
}
})
)
)
);
});
// Fetch event
self.addEventListener('fetch', (event) => {
if (event.request.method !== 'GET') return;
event.respondWith(
caches.match(event.request).then((cached) => {
return (
cached ||
fetch(event.request)
.then((res) => {
return caches.open(CACHE_NAME).then((cache) => {
cache.put(event.request, res.clone());
return res;
});
})
.catch(() => caches.match('/offline.html'))
);
})
);
});Add to public/index.html:
<script>
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/custom-sw.js').then(
function(reg) {
console.log('SW registered!', reg);
},
function(err) {
console.error('SW registration failed:', err);
}
);
}
</script>Add to index.js:
serviceWorkerRegistration.register({
onUpdate: (registration) => {
if (window.confirm('New version available. Refresh now?')) {
registration.waiting?.postMessage({ type: 'SKIP_WAITING' });
window.location.reload();
}
},
});Create public/manifest.json:
{
"short_name": "ReactPWA",
"name": "React App with PWA Support",
"icons": [
{
"src": "icon-192.png",
"type": "image/png",
"sizes": "192x192"
},
{
"src": "icon-512.png",
"type": "image/png",
"sizes": "512x512"
}
],
"start_url": ".",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}Add to public/index.html:
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<link rel="apple-touch-icon" href="%PUBLIC_URL%/icon-192.png" />
<meta name="theme-color" content="#000000" /># Build for production
npm run build
# Serve locally
npx serve -s buildVisit http://localhost:3000 and test offline functionality.
- Open Chrome DevTools
- Navigate to Application → Service Workers
- Use available controls:
- Update
- Unregister
- Offline mode
To remove service workers:
// Revert to unregister
serviceWorkerRegistration.unregister();
// Force unregister all service workers
if ('serviceWorker' in navigator) {
navigator.serviceWorker.getRegistrations().then((regs) => {
regs.forEach((reg) => reg.unregister());
});
}- Service Workers require HTTPS in production
- localhost is allowed for development
- External API caching needs CORS configuration
- Service Workers require manual reload for updates
- Create an
offline.htmlfallback page inpublic/directory