A Progressive Web App is a website that behaves like a real app on your phone. Here's what makes it different from a regular website:
Installs to home screenTap "Install" → icon on home screen, no app store
Works offlineOpen it with no internet, it still loads and works
No browser chromeLooks like a native app — no URL bar, no tabs
Auto-updatesPush a new file to server → user gets it automatically
TL;DR — A PWA makes your HTML page feel like a real app people download. Except it's just 3 files on a server.
What InteractiveLink Gives You Now
New: Each custom domain on InteractiveLink now gets 1 service worker (sw.js) and 1 manifest (manifest.json).
Before vs Now
Before ❌
You could upload HTML files
"Install" button just made a shortcut (a bookmark)
No offline support
No auto-updates — user had to manually reload
Still showed browser URL bar
Now ✅
Upload HTML + sw.js + manifest.json
"Install" gives a real PWA (standalone app, home screen icon)
Full offline support — opens without internet
Auto-updates when you push new files
No URL bar, looks like native app
Limit: 1 sw.js and 1 manifest.json per custom domain. If you want multiple PWA apps, use different domains or subdomains.
📄 yourapp.html
Your entire app. CSS, HTML, JavaScript — all in one file. This is what users see and interact with.
Name it whatever you want. We used ibadah.html.
⚙️ sw.js
Service Worker. This is the "magic" file that makes your app work offline and auto-update. It caches your HTML so it loads even without internet.
Must be named sw.js and live at domain root.
📋 manifest.json
Tells the browser "this is an app". Sets the app name, icon, colors, and makes the Install button appear.
Must be named manifest.json and live at domain root.
The AI Prompt — Copy & Paste This
Open your AI coding tool (GitHub Copilot Agent, Claude, ChatGPT, Cursor, etc.) and paste this prompt. It will generate all 3 files for you.
This is the actual prompt we used to build Ibadah Tracker (cleaned up). Modify the app name and features to match your idea.
Help me create a PWA app. Here are the requirements:
App concept:
- App name: Ibadah Tracker (change this to your app name)
- It tracks daily 5-time prayer, Quran reading, fasting, and sadaqah
- Add a hero section that shows a motivational Islamic quote and the user's daily stats (prayers done, ayat read, etc.)
- Track statistically interesting data (streaks, totals, averages) to show the user later
Architecture (IMPORTANT — follow exactly):
- Offline-first PWA that behaves like a real installed app
- Only 3 files: one HTML file (e.g. ibadah.html), sw.js, manifest.json
- The HTML file contains ALL CSS and JavaScript inline — no external files
- No frameworks, no build tools, no npm
- Use localStorage for all data storage (prefix all keys with "ibadah_")
- Use inline SVG data URIs for manifest icons — no image files
Service Worker (sw.js):
- Cache the app shell on install: './', './ibadah.html', './manifest.json'
- Network-first strategy for HTML and manifest.json (so users get fresh versions when online)
- Cache-first strategy for other assets (speed)
- Handle SKIP_WAITING message from the app (so app can control when to activate updates)
- Handle GET_VERSION and CLEAR_CACHE messages
- Use versioned cache names like 'myapp-v1.0.0' and clean old caches on activate
- Do NOT auto-call skipWaiting() on install — let the app decide when
manifest.json:
- display: standalone (no browser chrome)
- Use inline SVG for all icons (192x192 and 512x512 with purpose "any" + 512x512 maskable)
- Add app shortcuts for quick actions
- orientation: portrait-primary
Install UX:
- For Chrome/Edge: capture beforeinstallprompt, show custom install banner with dismiss option
- For Safari iOS: detect iOS via user agent, show instructions "Tap Share → Add to Home Screen"
- For Safari macOS: detect Mac Safari, show instructions "File → Add to Dock"
- Detect standalone mode to hide install banners if already installed
- Store dismiss state in localStorage so banners don't keep showing
Update system:
- Add APP_VERSION constant in the HTML file
- On startup: register service worker, call .update() immediately
- Listen for 'updatefound' event → show "Update Available" banner when new SW is waiting
- On visibility change: check for SW updates again
- Periodic check: every 5 minutes call serviceWorkerRegistration.update()
- Settings page with manual "Check for Updates" button that:
- Fetches all 3 files with cache:'no-store' and timestamp cache-busting
- Computes SHA-256 hash of each file
- Compares against stored hashes in localStorage
- Also regex-extracts APP_VERSION from fetched HTML and compares to running version
- Shows which files changed
- "Apply Update" button: sends SKIP_WAITING to SW, clears all caches, hard reloads
- Listen for 'controllerchange' on navigator.serviceWorker → reload page
Settings page should also have:
- Current version display
- Service Worker status
- Cache size info
- Clear cache button
- Force refresh button
- Export data as JSON backup
- Reset all data (with double confirmation)
Important for the HTML file:
- Use event delegation for any list that re-renders (e.g. prayer checkboxes) — attach click listener to the PARENT container, not individual items. This prevents the bug where you have to refresh after checking an item.
Placeholder for future:
- Online sync / user accounts (implement the UI placeholder, not the actual sync)
Generate all 3 complete files.
That's it. Paste this prompt, AI generates the 3 files, upload them to your InteractiveLink domain. You have a real PWA.
Customize it: Replace "Ibadah Tracker" with your app name, change the features to match what you're building. The architecture and technical requirements stay the same — that's what makes it a proper PWA.
Bugs You'll Probably Hit (We Did)
AI-generated code works 90% of the time. Here are the issues we had to fix after getting the initial code:
🐛 Bug 1: Prayer checkboxes need page refresh
What happens: You check a prayer, but can't check the next one without refreshing the page.
Why: The code attaches click listeners directly to each checkbox element. When the list re-renders (updates the HTML), those elements are destroyed and the listeners are gone.
Fix: Use event delegation — attach the listener to the parent container instead.
// ❌ BAD — listeners die on re-render
document.querySelectorAll('.prayer-check').forEach(el => {
el.addEventListener('click', () => toggle(el.dataset.prayer));
});
// ✅ GOOD — event delegation, survives re-render
document.getElementById('prayerList').addEventListener('click', (e) => {
const check = e.target.closest('.prayer-check');
if (check) toggle(check.dataset.prayer);
});
Tell the AI explicitly: "Use event delegation for any list that re-renders." We included this in the prompt above.
🐛 Bug 2: No install button on Safari
What happens: Users on iPhone/Safari don't see any way to install the app.
Why: Safari doesn't support beforeinstallprompt. That's Chrome/Edge only.
Fix: Detect Safari/iOS and show manual install instructions instead of a button.
What happens: You update sw.js or manifest.json but the app says "no updates available."
Why: Initial code may only check ibadah.html for changes, forgetting the other 2 files.
Fix: Tell AI to check all 3 files. Our update checker fetches ibadah.html, manifest.json, AND sw.js, hashes all three with SHA-256, and compares against stored hashes.
Pro tip: When you find a bug, tell the AI exactly what's wrong and ask it to fix it. Example: "The prayer checkboxes require a page refresh between checks. Fix using event delegation on the parent prayerList container."
How the App Actually Works
You don't need to understand all of this to use the prompt. But here's the quick version if you're curious.
The flow (5 steps)
1User opens your page — browser loads ibadah.html
2HTML registers sw.js — the browser downloads and installs the service worker
3SW caches files — saves ibadah.html and manifest.json locally on the device
4Next visit (even offline) — SW serves the cached files. App loads localStorage for data.
5You push an update — SW detects byte change in sw.js → app shows "Update Available" → user taps → reload with new version
What goes where
User datalocalStorage (prayers, stats, streaks) — survives cache clears
App filesSW cache (HTML, manifest) — makes it work offline
Version is hardcoded inside cache names. Update detected via SHA-256 hash (no parsing).
manifest.json
{ "name": "Ibadah Tracker", ... }
No version field at all. Update detected purely via SHA-256 hash.
localStorage keys we use
ibadah_stats — all user data (prayers, streaks, quran sessions, etc.)
ibadah_install_dismissed — Chrome install banner dismissed
ibadah_safari_install_dismissed — Safari banner dismissed
ibadah_last_update_check — timestamp of last manual check
ibadah_ibadah_hash — SHA-256 hash of ibadah.html
ibadah_manifest_hash — SHA-256 hash of manifest.json
ibadah_sw_hash — SHA-256 hash of sw.js
When releasing a new version
Change APP_VERSION in ibadah.html
Change CACHE_NAME and APP_SHELL_CACHE in sw.js
Upload all 3 files
Even if you forget to bump versions, the SHA-256 hash check catches any byte change. But bumping versions is good practice since the version number shows in Settings.
howismadeibadahpwa.html — A vibe coder's guide to building PWAs on InteractiveLink
Toggle ☀️/🌙 at the top. Built with Ibadah Tracker as the example.