For the full documentation index, see PageWeave Documentation or fetch llms.txt.

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

  1. Upload or pick an existing Website::Asset (PNG or SVG) for the app icon.
  2. Call update_website with pwa_manifest set to a valid Web App Manifest.
  3. Call update_html_head (or edit a page’s additional_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.js directly instead of using asset storage.