Progress Web Apps (PWA's)
Build a Progressive Web App with Aurelia 2 using modern tooling and 2025 best practices. PWAs provide native-like experiences with offline functionality, push notifications, and app-like installation.
Installation
Install the Vite PWA plugin for zero-config PWA setup:
npm install -D vite-plugin-pwa
Vite Configuration
Update your vite.config.ts
to include PWA capabilities:
// vite.config.ts
import { defineConfig } from 'vite';
import { VitePWA } from 'vite-plugin-pwa';
import aurelia from '@aurelia/vite-plugin';
export default defineConfig({
plugins: [
aurelia(),
VitePWA({
registerType: 'autoUpdate',
workbox: {
globPatterns: ['**/*.{js,css,html,ico,png,svg,woff2}'],
runtimeCaching: [
{
urlPattern: /^https:\/\/api\.yourapp\.com\/.*/i,
handler: 'NetworkFirst',
options: {
cacheName: 'api-cache',
networkTimeoutSeconds: 10,
cacheableResponse: {
statuses: [0, 200]
}
}
}
]
},
manifest: {
name: 'Aurelia PWA',
short_name: 'AureliaPWA',
description: 'My Aurelia Progressive Web App',
theme_color: '#ffffff',
background_color: '#ffffff',
display: 'standalone',
start_url: '/',
icons: [
{
src: '/icons/pwa-192x192.png',
sizes: '192x192',
type: 'image/png',
purpose: 'any'
},
{
src: '/icons/pwa-512x512.png',
sizes: '512x512',
type: 'image/png',
purpose: 'any'
},
{
src: '/icons/pwa-512x512.png',
sizes: '512x512',
type: 'image/png',
purpose: 'maskable'
}
]
}
})
]
});
Service Worker Integration
The Vite PWA plugin automatically handles service worker registration. To add update prompts in your Aurelia app:
// src/pwa-service.ts
import { DI, Registration } from '@aurelia/kernel';
import { registerSW } from 'virtual:pwa-register';
export const IPWAService = DI.createInterface<IPWAService>('IPWAService');
export interface IPWAService {
updateAvailable: boolean;
updateApp(): Promise<void>;
}
export class PWAService implements IPWAService {
updateAvailable = false;
private updateSW?: (reloadPage?: boolean) => Promise<void>;
constructor() {
this.updateSW = registerSW({
onNeedRefresh: () => {
this.updateAvailable = true;
},
onOfflineReady: () => {
console.log('App ready to work offline');
}
});
}
async updateApp(): Promise<void> {
if (this.updateSW) {
await this.updateSW(true);
}
}
}
export const PWAServiceRegistration = Registration.singleton(IPWAService, PWAService);
Register the service in your app:
// src/main.ts
import { Aurelia, StandardConfiguration } from '@aurelia/runtime-html';
import { PWAServiceRegistration } from './pwa-service';
import { MyApp } from './my-app';
const au = new Aurelia();
au.register(
StandardConfiguration,
PWAServiceRegistration
);
au.app({ host: document.querySelector('my-app'), component: MyApp });
await au.start();
Update Component
Create a component to handle PWA updates:
// src/components/pwa-update.ts
import { customElement } from '@aurelia/runtime-html';
import { resolve } from '@aurelia/kernel';
import { IPWAService } from '../pwa-service';
@customElement('pwa-update')
export class PWAUpdate {
private readonly pwaService: IPWAService = resolve(IPWAService);
get updateAvailable() {
return this.pwaService.updateAvailable;
}
async updateApp() {
await this.pwaService.updateApp();
}
}
<!-- src/components/pwa-update.html -->
<div if.bind="updateAvailable" class="update-banner">
<p>New version available!</p>
<button click.trigger="updateApp()">Update</button>
</div>
Icon Setup
Create properly sized icons for your PWA. The plugin expects icons in /public/icons/
:
public/
└── icons/
├── pwa-192x192.png # Required: 192x192 icon
├── pwa-512x512.png # Required: 512x512 icon (also used as maskable)
├── apple-touch-icon.png
└── favicon.ico
Maskable Icons
Create maskable icons with proper safe zone padding. Use maskable.app to test your icons. The important content should be within a circular area with 40% radius from the center.
Development and Testing
Development Mode
Enable PWA features in development:
// vite.config.ts - add to VitePWA options
devOptions: {
enabled: true,
type: 'module'
}
Testing Your PWA
Build for production:
npm run build
Serve the built app:
npm run preview
Test PWA features:
Use Chrome DevTools → Lighthouse for PWA audit
Check Application tab for manifest and service worker
Test offline functionality by toggling network
Verify installation prompt appears
Advanced Caching Strategies
For custom caching needs, create a custom service worker:
// vite.config.ts
VitePWA({
strategies: 'injectManifest',
srcDir: 'src',
filename: 'sw.ts',
// ... other options
})
// src/sw.ts
import { precacheAndRoute, cleanupOutdatedCaches } from 'workbox-precaching';
import { registerRoute } from 'workbox-routing';
import { StaleWhileRevalidate, CacheFirst } from 'workbox-strategies';
declare let self: ServiceWorkerGlobalScope;
// Precache all static assets
precacheAndRoute(self.__WB_MANIFEST);
cleanupOutdatedCaches();
// Cache API responses
registerRoute(
({ url }) => url.pathname.startsWith('/api/'),
new StaleWhileRevalidate({
cacheName: 'api-cache'
})
);
// Cache images
registerRoute(
({ request }) => request.destination === 'image',
new CacheFirst({
cacheName: 'images-cache',
plugins: [
{
cacheMaxEntries: 50,
cacheMaxAgeSeconds: 30 * 24 * 60 * 60 // 30 days
}
]
})
);
Best Practices
HTTPS Required - PWAs only work over HTTPS (or localhost for development)
Responsive Design - Ensure your app works on all screen sizes
Performance - Optimize Core Web Vitals for app-like experience
Offline Fallbacks - Provide meaningful offline pages and functionality
Update Strategy - Choose between
autoUpdate
andprompt
based on user experience needs
Your Aurelia 2 app is now a fully-featured PWA with modern caching strategies, update management, and native-like installation capabilities.
Last updated
Was this helpful?