PWA Support
DotSite websites can be turned into Progressive Web Apps (PWAs). The platform provides two same-origin endpoints:
/manifest.webmanifest— raw Web App Manifest JSON. Free./sw.js— raw service worker JavaScript. Pro-only.
Set the content of these endpoints via MCP update_website, then wire them into html_head.
Enabling PWA
- Upload or pick an existing
Website::Asset(PNG or SVG) for the app icon. - Call
update_websitewithpwa_manifestset to a valid Web App Manifest. - Call
update_html_head(or edit a page’sadditional_html_head_html) to add:
<link rel="manifest" href="/manifest.webmanifest">
<meta name="theme-color" content="#3b82f6">
<link rel="apple-touch-icon" href="/assets/{icon-asset-id}/icon-192.png">
The site is now installable on supported browsers.
Enabling offline support (Pro)
On a Pro website, also set pwa_service_worker and register it from html_head:
<script>
if ("serviceWorker" in navigator) {
navigator.serviceWorker.register("/sw.js").catch(() => {});
}
</script>
On free plans /sw.js returns 404, so the register() promise rejects. The catch above keeps the console clean.
Minimal manifest
{
"name": "My Site",
"short_name": "My Site",
"start_url": "/",
"display": "standalone",
"scope": "/",
"theme_color": "#3b82f6",
"background_color": "#ffffff",
"icons": [
{ "src": "/assets/{uuid}/icon-192.png", "sizes": "192x192", "type": "image/png" },
{ "src": "/assets/{uuid}/icon-512.png", "sizes": "512x512", "type": "image/png" }
]
}
Recommended service worker
A low-maintenance, network-first strategy for dynamic DotSite content. Previously visited pages work offline; assets are cached because their URLs are immutable (UUID-based).
const CACHE_NAME = "dotsite-runtime-v1";
async function networkFirst(request) {
const cache = await caches.open(CACHE_NAME);
try {
const networkResponse = await fetch(request);
if (networkResponse && networkResponse.status === 200 && request.method === "GET") {
cache.put(request, networkResponse.clone());
}
return networkResponse;
} catch (error) {
const cached = await cache.match(request);
if (cached) return cached;
throw error;
}
}
async function cacheFirst(request) {
const cache = await caches.open(CACHE_NAME);
const cached = await cache.match(request);
return cached || fetch(request);
}
self.addEventListener("fetch", (event) => {
if (event.request.method !== "GET") return;
const url = new URL(event.request.url);
if (url.origin !== location.origin) return;
if (event.request.mode === "navigate") {
event.respondWith(networkFirst(event.request));
} else {
event.respondWith(cacheFirst(event.request));
}
});
To force an update for installed users, change any byte in pwa_service_worker — for example, bump CACHE_NAME to v2. The platform serves /sw.js with Cache-Control: no-cache so browsers always byte-compare it.
Disabling PWA
Call update_website with pwa_manifest: "" (and pwa_service_worker: "" on Pro). The routes return 404 and browsers stop promoting install.
Notes
- Both fields are scanned by the prohibited-content filter at save time.
- No structural validation is performed on the JSON or JS. A malformed manifest is ignored by the browser; a malformed service worker logs an error in the browser console.
- Service workers must be served from the same origin as the site. This is why the platform provides
/sw.jsdirectly instead of using asset storage.