PWAs promised app-like experiences on the web. Years later, adoption is mixed. With Apple's grudging improvements and new capabilities, it's time to reassess: when do PWAs make sense?
The Current State of PWA Support
PWA capabilities have expanded significantly:
- iOS: Push notifications finally supported (Safari 16.4+). Still limitations: no background sync, limited storage
- Android: Full feature support, indistinguishable from native in many cases
- Desktop: Excellent support on Chrome, Edge. Good on Firefox, Safari improving
What PWAs Can Do Now
// Service worker for offline support
self.addEventListener('fetch', (event) => {
event.respondWith(
caches.match(event.request)
.then(response => response || fetch(event.request))
)
})
// Push notifications
self.addEventListener('push', (event) => {
const data = event.data.json()
event.waitUntil(
self.registration.showNotification(data.title, {
body: data.body,
icon: '/icon-192.png',
badge: '/badge.png',
})
)
})
// Background sync (Android/Desktop)
self.addEventListener('sync', (event) => {
if (event.tag === 'sync-messages') {
event.waitUntil(syncMessages())
}
})
PWA vs Native: Decision Framework
function shouldUsePWA(requirements) {
const pwaScore = {
// PWA strengths
crossPlatform: requirements.platforms.length > 1 ? 2 : 0,
webFirst: requirements.existingWebApp ? 2 : 0,
updateFrequency: requirements.frequentUpdates ? 2 : 0,
discoverability: requirements.seoImportant ? 2 : 0,
// PWA limitations
advancedHardware: requirements.bluetooth || requirements.nfc ? -2 : 0,
backgroundProcessing: requirements.heavyBackground ? -2 : 0,
appStorePresence: requirements.appStoreRequired ? -1 : 0,
iosFirst: requirements.primaryPlatform === 'ios' ? -1 : 0,
}
const total = Object.values(pwaScore).reduce((a, b) => a + b, 0)
return {
recommendation: total > 2 ? 'PWA' : total < -2 ? 'Native' : 'Hybrid',
score: total,
}
}
Offline-First Architecture
// Workbox for sophisticated caching
import { precacheAndRoute } from 'workbox-precaching'
import { registerRoute } from 'workbox-routing'
import { StaleWhileRevalidate, CacheFirst } from 'workbox-strategies'
// Precache app shell
precacheAndRoute(self.__WB_MANIFEST)
// API responses: stale-while-revalidate
registerRoute(
({ url }) => url.pathname.startsWith('/api/'),
new StaleWhileRevalidate({ cacheName: 'api-cache' })
)
// Images: cache-first
registerRoute(
({ request }) => request.destination === 'image',
new CacheFirst({
cacheName: 'images',
plugins: [new ExpirationPlugin({ maxEntries: 100 })],
})
)
When PWAs Excel
- Content-focused apps (news, blogs, documentation)
- E-commerce with need for discoverability
- Internal enterprise tools
- Apps that need frequent updates without app store review
- Markets where app store presence isn't expected
When to Go Native
- Heavy reliance on device hardware (AR, advanced sensors)
- Significant background processing needs
- iOS-first user base with high expectations
- App store discoverability is critical to acquisition
Key Takeaways
PWAs have matured significantly. Push notifications on iOS was the last major missing feature.
The gap is narrowing. For many use cases, PWAs are now indistinguishable from native.
Platform fit matters. Android/Desktop: excellent. iOS: good but not great.
Consider hybrid. PWA for web + native shells (Capacitor) for app stores.
