Membuat aplikasi web berfungsi penuh offline dengan PWA: service workers dan siklus hidupnya, strategi caching (network-first, cache-first, stale-while-revalidate), kriteria installability, dan penerapan offline di TokoKita

Koneksi internet tidak selalu stabil — terutama untuk pengguna mobile. Episode 22 membahas PWA (Progressive Web Apps): aplikasi web yang bisa diinstall, bekerja offline, dan dimuat seketika berkat service workers. Ini bukan tren: PWA adalah strategi bisnis nyata untuk produk yang menjangkau pengguna di koneksi lambat.
Mengapa episode ini penting untuk TokoKita? Karena pengunjung dengan koneksi buruk adalah pengguna nyata. Aplikasi yang "berfungsi tanpa internet" untuk konten yang pernah dibuka, plus ikon di homescreen, mengubah web dari "halaman" menjadi "aplikasi" — pengalaman yang pengguna harapkan dari produk serius.
PWA adalah web app dengan tiga syarat inti:
Web app "biasa" yang dipakai tanpa koneksi → blank page. PWA yang pernah dikunjungi → tetap berfungsi dari cache.
Service worker adalah script yang berjalan terpisah dari halaman dan bertindak sebagai proxy antara browser dan jaringan. Ia menjalankan tiga event utama:
// INSTALL — simpan aset inti (shell aplikasi)
self.addEventListener("install", (event) => {
event.waitUntil(
caches.open("toko-kita-v1").then((cache) =>
cache.addAll(["/", "/products", "/offline.html"])
)
);
self.skipWaiting();
});
// ACTIVATE — bersihkan cache versi lama
self.addEventListener("activate", (event) => {
event.waitUntil(
caches.keys().then((keys) =>
Promise.all(
keys
.filter((k) => k !== "toko-kita-v1")
.map((k) => caches.delete(k))
)
)
);
});
// FETCH — putuskan: cache, jaringan, atau keduanya
self.addEventListener("fetch", (event) => {
event.respondWith(handleFetch(event.request));
});Mengapa service worker butuh HTTPS? Karena ia bisa mencegat semua request — browser tidak mengizinkan proxy ini di halaman non-secure.
Note
Perhatikan versi cache (toko-kita-v1). Saat aset berubah, ganti nama versi — service worker lalu membersihkan yang lama saat activate. Inilah mekanisme utama menghindari pengguna "terkunci" di versi lama aplikasi.
Empat pola umum — pilih sesuai jenis konten:
| Strategi | Alur | Cocok untuk |
|---|---|---|
| Cache-first | Cache dulu, fallback jaringan | Aset statis (CSS, gambar, ikon) |
| Network-first | Jaringan dulu, fallback cache | API & halaman yang butuh data segar |
| Stale-while-revalidate | Cache dulu, perbarui di latar | Konten yang boleh sedikit basi |
| Network-only | Jangan pernah cache | Mutasi & data sensitif (checkout) |
async function handleFetch(request: Request): Promise<Response> {
const cache = await caches.open("toko-kita-v1");
try {
const res = await fetch(request);
if (res.ok) cache.put(request, res.clone());
return res;
} catch {
return (await cache.match(request)) || caches.match("/offline.html");
}
}Pola ini membuat halaman selalu segar saat online, dan tetap muncul saat offline. Perhatikan fallback terakhir: halaman offline.html yang ramah (bukan error browser).
Manifest membuat aplikasi "installable" — dengan ikon, nama, dan mode tampilan:
{
"name": "TokoKita",
"short_name": "TokoKita",
"description": "Toko online sederhana",
"start_url": "/",
"display": "standalone",
"background_color": "#ffffff",
"theme_color": "#b45309",
"icons": [
{ "src": "/icon-192.png", "sizes": "192x192", "type": "image/png" },
{ "src": "/icon-512.png", "sizes": "512x512", "type": "image/png" }
]
}if ("serviceWorker" in navigator) {
window.addEventListener("load", () => {
navigator.serviceWorker.register("/sw.js");
});
}Browser mengizinkan install prompt saat: HTTPS, manifest valid, ikon ada, dan ada service worker dengan fetch handler.
Di Next.js, pendekatan praktis 2026:
next-pwa — membungkus setup service worker otomatis (Workbox).next/offline (Next.js 15+) — eksperimental, integrasi bawaan.sw.ts sendiri untuk kontrol penuh (belajar paling banyak).Statis (/_next/static, gambar, font) → cache-first
Halaman publik (/products) → stale-while-revalidate
API produk → network-first
Checkout & auth → network-only (jangan cache!)| Kesalahan | Dampak | Perbaikan |
|---|---|---|
| Cache semua request termasuk mutasi | Data aneh/duplikat | Network-only untuk POST/auth |
| Cache versi tidak diatur | Pengguna stuck versi lama | Versioning cache + cleanup |
| Service worker tanpa fallback offline | Error browser yang menakutkan | Halaman offline.html yang ramah |
Cache API yang salah | Harga/stok basi | TTL atau network-first |
| Registrasi di halaman non-HTTPS | Tidak pernah jalan | HTTPS wajib |
Inti yang harus dibawa pulang:
Di episode 23 selanjutnya kita membuat data bisa dibaca: data visualization — charts dengan Recharts/D3, dashboard interaktif, dan performa rendering data TokoKita. Sampai jumpa di episode 23!