Cara Bikin Progressive Web App (PWA) dari Nol 2026
Oke, jadi ceritanya kamu lagi pengin bikin web app yang bisa diinstall di HP kayak aplikasi biasa, bisa jalan offline, dan nggak ribet harus publish ke Play Store atau App Store? Nah, jawabannya adalah Progressive Web App (PWA).
Gue nulis ini karena waktu pertama kali coba bikin PWA, gue lumayan bingung. Dokumentasinya tersebar, banyak tutorial lama yang udah nggak relevan (apalagi dengan ekosistem 2026 yang udah beda banget), dan kadang penjelasannya terlalu “textbook” — bikin ngantuk, bukannya bikin paham.
Jadi di artikel ini, gue mau share step-by-step cara bikin PWA dari nol. Beneran nol. Nggak perlu framework macem-macem. Kita mulai dari HTML polos, lalu pelan-pelan tambahin fitur PWA-nya. Setelah baca ini, kamu harusnya udah bisa bikin PWA pertama kamu sendiri.
Siap? Gas.
Apa Itu PWA dan Kenapa Kamu Harus Peduli di 2026?
Sebelum masuk ke kode, gue mau bahas dulu kenapa kamu harus repot-repot bikin PWA. Soalnya kalau nggak paham motivasinya, nanti pas di tengah jalan kamu bakal males lanjut.
PWA itu intinya web biasa yang diperlakukan kayak native app. Dia bisa:
- Diinstall ke home screen HP atau desktop tanpa lewat app store
- Jalan offline (atau minimal menunjukkan halaman yang proper kalau koneksi mati)
- Push notification kayak aplikasi native
- Lebih cepat karena caching yang cerdas
- Aman karena wajib pakai HTTPS
Di tahun 2026, PWA makin relevan. Kenapa? Karena sekarang:
- Browser makin powerful. Dukungan terhadap API PWA di Chrome, Edge, Firefox, dan bahkan Safari udah jauh lebih baik dibanding 2-3 tahun lalu. Safari yang dulu terkenal pelit support, sekarang udah support push notification dan banyak API lainnya.
- Biaya develop native app makin gila. Kamu perlu developer Android, developer iOS, maintain dua codebase. PWA? Satu codebase, jalan di mana-mana.
- User experience makin seamless. Install prompt-nya udah lebih halus, offline mode-nya lebih reliable, dan performanya bisa diadu sama app native untuk kasus penggunaan yang tepat.
Jadi kalau kamu lagi bikin MVP, project sampingan, landing page interaktif, atau bahkan aplikasi bisnis — PWA layak banget dipertimbangkan.
Persiapan: Apa yang Kamu Butuhkan
Nggak banyak, kok. Ini daftarnya:
- Text editor — VS Code, Sublime, atau bahkan Notepad juga bisa
- Browser modern — Chrome atau Edge recommended (dev tools-nya paling lengkap buat debug PWA)
- Node.js (opsional) — kita butuh ini buat local server. Alternatifnya, kamu bisa pakai extension “Live Server” di VS Code
- HTTPS — Service worker cuma jalan di HTTPS. Untuk development di localhost, kamu nggak perlu ini. Tapi pas nanti deploy, wajib hukumnya
- Semangat dan kopi — ini non-negotiable
Kalau semua udah ready, yuk mulai bikin.
Langkah 1: Setup Project dan Bikin Web App Dasar
Pertama-tama, buat folder project baru. Misalnya kita kasih nama pwa-from-scratch.
Untuk icon-nya, kamu bisa generate pakai tools online atau pakai icon default dulu buat testing. Yang penting ada file-nya.
index.html
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="theme-color" content="#4f46e5">
<meta name="description" content="PWA pertama gue, dari nol!">
<!-- Link ke manifest -->
<link rel="manifest" href="manifest.json">
<!-- Favicon & icon -->
<link rel="icon" href="icons/icon-192.png" type="image/png">
<link rel="apple-touch-icon" href="icons/icon-192.png">
<title>PWA dari Nol</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="container">
<h1>🚀 PWA Pertama Gue</h1>
<p>Ini adalah web app sederhana yang udah bisa diinstall!</p>
<div class="status" id="status">
Status: <span id="online-status">Online</span>
</div>
<button id="install-btn" style="display:none;">Install Aplikasi</button>
</div>
<script src="app.js"></script>
</body>
</html>
style.css
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background: linear-gradient(135deg, #4f46e5, #7c3aed);
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
color: #fff;
}
.container {
text-align: center;
padding: 2rem;
max-width: 500px;
}
h1 {
font-size: 2.5rem;
margin-bottom: 1rem;
}
.status {
margin: 1.5rem 0;
padding: 0.75rem 1.5rem;
background: rgba(255, 255, 255, 0.15);
border-radius: 12px;
display: inline-block;
}
button {
padding: 1rem 2rem;
font-size: 1.1rem;
border: none;
border-radius: 12px;
background: #fff;
color: #4f46e5;
font-weight: 700;
cursor: pointer;
transition: transform 0.2s;
}
button:hover {
transform: scale(1.05);
}
Kalau kamu buka index.html di browser sekarang, kamu bakal lihat halaman cantik dengan gradient ungu. Tapi ini belum PWA. Ini masih web biasa. Sabar, kita tambahin fitur PWA-nya pelan-pelan.
Langkah 2: Membuat Web App Manifest
Web App Manifest adalah file JSON yang kasih tau browser: “Hei, web ini tuh sebenernya app. Ini nama-nya, ini icon-nya, ini warna tema-nya.”
Tanpa manifest, browser nggak bakal nge-recognize web kamu sebagai installable app.
manifest.json
{
"name": "PWA dari Nol 2026",
"short_name": "PWA Nol",
"description": "Contoh PWA sederhana dari nol",
"start_url": "/index.html",
"display": "standalone",
"background_color": "#4f46e5",
"theme_color": "#4f46e5",
"orientation": "portrait-primary",
"icons": [
{
"src": "icons/icon-192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "any maskable"
},
{
"src": "icons/icon-512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "any maskable"
}
]
}
Penjelasan singkat tiap properti:
| Properti | Fungsi |
|---|---|
name | Nama lengkap yang muncul pas install |
short_name | Nama pendek di home screen |
start_url | Halaman pertama yang dibuka pas app diluncurkan |
display | standalone biar nggak ada address bar, kayak app beneran |
background_color | Warna background pas app loading |
theme_color | Warna status bar di mobile |
icons | Icon yang dipake buat splash screen dan home screen |
Tips: Di 2026 ini, properti purpose: "any maskable" udah lebih umum didukung. maskable itu penting biar icon-nya nggak kepotong aneh-aneh di berbagai device. Pastikan icon kamu punya padding yang cukup (sekitar 10-15% dari ukuran) biar aman di-crop.
Langkah 3: Implementasi Service Worker — Ini yang Bikin PWA Jadi “PWA”
Nah, ini bagian paling penting dan paling sering bikin orang bingung. Service Worker itu semacam proxy yang berjalan di background, antara browser dan internet. Dia yang bikin app kamu bisa:
- Cache file biar load lebih cepat
- Tetap jalan walau koneksi mati
- Handle push notification
Registrasi Service Worker di app.js
// app.js
// === Registrasi Service Worker ===
if ('serviceWorker' in navigator) {
window.addEventListener('load', async () => {
try {
const registration = await navigator.serviceWorker.register('/sw.js');
console.log('Service Worker registered!', registration.scope);
} catch (error) {
console.error('Service Worker registration failed:', error);
}
});
}
// === Online/Offline Status ===
const statusEl = document.getElementById('online-status');
function updateStatus() {
statusEl.textContent = navigator.onLine ? '🟢 Online' : '🔴 Offline';
statusEl.style.color = navigator.onLine ? '#86efac' : '#fca5a5';
}
window.addEventListener('online', updateStatus);
window.addEventListener('offline', updateStatus);
updateStatus();
// === Install Prompt ===
let deferredPrompt;
const installBtn = document.getElementById('install-btn');
window.addEventListener('beforeinstallprompt', (e) => {
e.preventDefault();
deferredPrompt = e;
installBtn.style.display = 'inline-block';
installBtn.addEventListener('click', async () => {
deferredPrompt.prompt();
const result = await deferredPrompt.userChoice;
console.log('User choice:', result.outcome);
deferredPrompt = null;
installBtn.style.display = 'none';
});
});
Service Worker File — sw.js
Ini bagian yang agak tricky. Di 2026, approach yang gue recommend adalah Cache-first dengan network fallback untuk aset statis, dan Network-first untuk data dinamis.
// sw.js
const CACHE_NAME = 'pwa-nol-v1';
const ASSETS_TO_CACHE = [
'/',
'/index.html',
'/style.css',
'/app.js',
'/manifest.json',
'/icons/icon-192.png',
'/icons/icon-512.png'
];
// === INSTALL: Cache semua aset penting ===
self.addEventListener('install', (event) => {
console.log('[Service Worker] Installing...');
event.waitUntil(
caches.open(CACHE_NAME).then((cache) => {
console.log('[Service Worker] Caching assets...');
return cache.addAll(ASSETS_TO_CACHE);
})
);
// Langsung aktif tanpa nunggu tab lama ditutup
self.skipWaiting();
});
// === ACTIVATE: Hapus cache lama ===
self.addEventListener('activate', (event) => {
console.log('[Service Worker] Activating...');
event.waitUntil(
caches.keys().then((cacheNames) => {
return Promise.all(
cacheNames
.filter((name) => name !== CACHE_NAME)
.map((name) => caches.delete(name))
);
})
);
// Ambil kontrol semua tab yang terbuka
self.clients.claim();
});
// === FETCH: Intercept semua request ===
self.addEventListener('fetch', (event) => {
// Hanya handle GET request
if (event.request.method !== 'GET') return;
event.respondWith(
caches.match(event.request).then((cachedResponse) => {
if (cachedResponse) {
// Update cache di background (stale-while-revalidate)
event.waitUntil(
fetch(event.request).then((networkResponse) => {
caches.open(CACHE_NAME).then((cache) => {
cache.put(event.request, networkResponse);
});
}).catch(() => {
// Network gagal, nggak apa-apa, kita udah punya cache
})
);
return cachedResponse;
}
// Kalau belum ada di cache, fetch dari network
return fetch(event.request).then((networkResponse) => {
// Clone response karena dia cuma bisa dibaca sekali
const responseClone = networkResponse.clone();
caches.open(CACHE_NAME).then((cache) => {
cache.put(event.request, responseClone);
});
return networkResponse;
}).catch(() => {
// Network gagal dan nggak ada cache — fallback page
if (event.request.destination === 'document') {
return caches.match('/index.html');
}
});
})
);
});
Penjelasan flow-nya:
- Install → Service worker diinstall dan langsung cache semua file yang kita definisikan
- Activate → Service worker membersihkan cache-versi-lama yang udah nggak dipake
- Fetch → Setiap kali browser minta file, service worker intercept:
- Ada di cache? Langsung kasih dari cache. Sambil update di background (ini namanya stale-while-revalidate)
- Nggak ada di cache? Fetch dari network, simpan ke cache buat next time
- Network mati dan nggak ada cache? Kasih fallback page
Strategi ini bikin app kamu super cepat karena sebagian besar request dipenuhi dari cache, tapi tetap fresh karena selalu update di background.
Langkah 4: Testing — Pastikan PWA Kamu Beneran Jalan
Sekarang bagian seru: ngetes.
Jalankan Local Server
Service worker butuh HTTPS (atau localhost). Jadi kamu nggak bisa buka langsung file:// di browser.
Cara termudah:
# Kalau udah install Node.js
npx serve .
Atau pakai Live Server di VS Code.
Pakai Lighthouse
Buka Chrome DevTools → tab Lighthouse → centang Progressive Web App → klik Analyze.
Lighthouse bakal kasih skor dan checklist. Ini yang harusnya hijau semua:
- ✅ Installable (manifest valid + service worker registered)
- ✅ Works offline
- ✅ Uses HTTPS
- ✅ Has proper icons
- ✅ Viewport meta tag present
Manual Test
- Buka app di Chrome
- Klik ikon install di address bar (atau tombol yang kita buat)
- App ke-install! Cek di home screen / app drawer
- Buka app, matikan internet, refresh — harusnya tetap jalan
- Buka DevTools → Application tab → Service Workers — pastikan status “activated and is running”
Kalau semuanya oke, selamat — kamu baru aja bikin PWA pertama kamu! 🎉
Tips & Best Practices Biar PWA Kamu Nggak Setengah-Setengah
1. Jangan Lupa Update Cache Version
const CACHE_NAME = 'pwa-nol-v2'; // Naikin versi tiap update
Kalau kamu update file tapi versi cache nggak berubah, user bakal tetap lihat versi lama. Ini masalah klasik yang sering bikin orang kesel.
2. Gunakan purpose: "any maskable" yang Benar
Di manifest.json, purpose: "any maskable" artinya icon bisa dipake buat adaptive icon (maskable) sekaligus icon biasa. Pastikan desain icon-nya aman di-crop dari berbagai bentuk.
3. Tambahkan Shortcut di Manifest
"shortcuts": [
{
"name": "Halaman Tentang",
"short_name": "Tentang",
"url": "/about.html",
"icons": [{ "src": "icons/icon-96.png", "sizes": "96x96" }]
}
]
Shortcut muncul kalau user long-press icon app di home screen. Mirip kayak menu klik-kanan di desktop.
4. Service Worker: Jangan Cache Terlalu Banyak
Cache semua file mungkin oke buat app kecil, tapi buat app yang lebih besar, pertimbangkan:
- Cache halaman yang sering dikunjungi aja
- Jangan cache data sensitif
- Setelah limit cache, hapus entry terlama (cache expiration)
5. Perhatikan UX Pas Offline
Kasih tau user kalau mereka lagi offline. Jangan diam-diam aja kasih data stale tanpa info. Pattern yang bagus:
// Tambahkan banner di bagian atas halaman
function showOfflineBanner() {
const banner = document.createElement('div');
banner.textContent = '📡 Kamu sedang offline. Data yang ditampilkan mungkin tidak terbaru.';
banner.style.cssText = 'background:#f59e0b;color:#000;padding:8px;text-align:center;font-size:14px;';
document.body.prepend(banner);
}
Bonus: Deploy ke Netlify / Vercel (Gratis!)
Mau app kamu bisa diakses orang? Deploy ke Netlify atau Vercel — keduanya kasih HTTPS gratis dan support SPA/PWA out of the box.
Netlify:
- Push project ke GitHub
- Login ke netlify.com → “Add new site” → pilih repo
- Klik deploy. Selesai. HTTPS otomatis.
Vercel:
- Push ke GitHub
npx verceldi terminal, atau link repo lewat vercel.com- Done.
Setelah deploy, buka URL-nya di HP, dan kamu bakal lihat install prompt muncul. Mantap, kan?
Butuh Bantuan atau Konsultasi?
Kalau kamu udah coba langkah-langkah di atas tapi masih stuck, atau mau bikin PWA yang lebih advanced — misalnya ada push notification, background sync, atau integrasi sama database — jangan ragu buat reach out.
Email gue di [email protected] — bisa buat tanya-tanya, diskusi project, atau bahkan minta bantuan bikin PWA untuk bisnis kamu. Gratis buat konsultasi awal, kok.
FAQ (Pertanyaan yang Sering Ditanyain)
PWA bisa sepenuhnya menggantikan aplikasi native?
Jawaban jujurnya: tergantung kebutuhan. Kalau app kamu butuh akses ke hardware yang sangat spesifik (NFC, Bluetooth tingkat rendah, sensor khusus), native masih lebih unggul. Tapi untuk 80-90% use case — e-commerce, blog interaktif, dashboard, to-do app, productivity tool — PWA di tahun 2026 udah lebih dari cukup. Plus, kamu nggak perlu repot urus app store approval yang kadang bikin frustrasi.
Kenapa service worker saya nggak jalan?
Ini masalah paling umum. Cek beberapa hal ini:
- Pastikan HTTPS — Service worker cuma jalan di secure context (HTTPS atau localhost)
- Path file benar —
sw.jsharus di root folder, bukan di subfolder (atau sesuaikan scope-nya) - Syntax error — Buka DevTools → Console, biasanya ada error merah yang kasih tau masalahnya
- Cache nggak ke-update — Di DevTools → Application → Service Workers, centang “Update on reload” buat development
Apakah PWA bisa publish ke Play Store?
Bisa! Google memperbolehkan Trusted Web Activity (TWA) untuk publish PWA ke Play Store. Kamu perlu bikin wrapper Android minimal yang nge-load URL PWA kamu. Tools kayak PWABuilder bisa bantu generate file APK/AAB yang siap upload. Jadi kamu tetap bisa distribusi lewat Play Store tanpa develop native Android.
Berapa ukuran maksimal cache service worker?
Browser umumnya ngasih limit sekitar 6% dari total disk space yang tersedia untuk satu origin. Jadi kalau user punya HP dengan 100GB free space, kamu bisa cache sampai ~6GB. Tapi jangan lakukan itu. Cache seperlunya aja. Idealnya, aset penting (HTML, CSS, JS, icon) di-cache duluan, dan asset lain di-cache on-demand.
Penutup
Bikin PWA dari nol itu ternyata nggak serumit yang dibayangkan. Yang kamu butuhkan hanyalah:
- Manifest — biar browser tau ini app
- Service Worker — biar bisa offline dan cepat
- HTTPS — biar secure
Tiga komponen itu, ditambah web app yang udah kamu bikin, udah cukup buat jadi PWA. Selebihnya, tinggal polish — tambahin push notification, background sync, atau bahkan payment request API.
Di 2026 ini, barrier to entry buat PWA makin rendah. Browser makin supportive, tools makin banyak, dan user juga udah lebih familiar dengan konsep “install dari browser.”
Jadi, nggak ada alasan buat nggak coba. Mulai dari yang sederhana, iterasi pelan-pelan, dan lihat hasilnya.
Selamat coding! 🚀