DEV Community

Coloful Toolhub
Coloful Toolhub

Posted on

Ship an installable PWA: manifest, maskable icons, and a service worker

This is the short path that actually makes a static site installable. The generators used below run locally in the browser on Colorful Toolhub's Mobile PWA workbench. You still deploy the files yourself.

Chromium installs a site when it is served over HTTPS, the page points at a valid manifest, a service worker controls the page, and 192px and 512px icons are present. iOS does not use the manifest icons for the home screen. You still need apple-touch-icon.

Manifest

Use the PWA Manifest Generator.

  • short_name stays around 12 characters. Home screens truncate longer names.
  • start_url is a stable path inside the site, optionally with one fixed query such as /?source=pwa. Do not put changing analytics parameters here.
  • scope must cover start_url. A site deployed at /app/ cannot use scope /.
  • display: "standalone" is the usual choice. browser does not leave the browser UI. fullscreen hides the status bar.
  • background_color is the splash background. theme_color is the browser chrome. They can differ.
  • Provide PNG icons at 192 and 512. SVG alone is still unreliable on some Android installers.

shortcuts are optional. Each shortcut URL has to sit inside scope.

Save the file as /manifest.json and add this to every HTML document:

<link rel="manifest" href="/manifest.json">
Enter fullscreen mode Exit fullscreen mode

A root-relative href that does not match the deployed path fails silently. The installer requests that exact URL.

Maskable icons

Android masks icons as a circle, a rounded square, or a teardrop. The safe zone is roughly the center 80%. Keep the mark inside that circle.

Adjust it in the Maskable Icon Editor, and preview circle, rounded-rect, and teardrop. Prefer two manifest entries over a single purpose: "any maskable" image:

{ "src": "/icon-192.png", "sizes": "192x192", "type": "image/png", "purpose": "any" },
{ "src": "/icon-maskable-192.png", "sizes": "192x192", "type": "image/png", "purpose": "maskable" }
Enter fullscreen mode Exit fullscreen mode

Do the same for 512. One image marked any maskable is often padded, so the non-masked icon looks small.

iOS still wants touch icons and splash screens. The Launch Icon and Splash Generator exports those PNGs and the link tags. Without them, Safari screenshots the current page and flashes a blank splash.

Service worker

Installability only needs a controlling worker. Caching strategy decides whether the next deploy is reachable.

  • Fingerprinted JS, CSS, and fonts: cache-first. A new filename is a new resource.
  • HTML: network-first, then cache. Cache-first HTML keeps pointing at deleted hashed assets and the site goes blank.
  • API responses: stale-while-revalidate, or do not cache them.
  • One offline fallback document for failed navigations.

The Service Worker Generator emits sw.js plus a registration snippet for those strategies. Register after load. The default scope is the script's directory, so /sw.js can control the origin and /assets/sw.js cannot.

Do not call skipWaiting() and clients.claim() on the first install of every version. The old page and the new cache will mix. Let the new worker wait, tell the page, and skip waiting only after a refresh.

Check

In Chrome DevTools, Application:

  • Manifest shows no errors, start_url is inside scope, icons preview.
  • The service worker is activated and controls the current window.
  • Offline reload still serves the HTML fallback you wrote, not the browser's offline dinosaur.

The install icon, or a firing beforeinstallprompt, is the real signal. Lighthouse no longer scores PWA as a single category, so an old PWA score is not the definition of done.

Top comments (0)