Installable app (PWA)

Turn the site into an app people install from their phone's home screen or as a desktop app. You give it a name, colors and one icon; Arc CMS builds the rest.

What it is

With the PWA on, the build adds a web app manifest, every icon size, and a service worker. People get:

Admins see install numbers on the admin dashboard. For when an app should use this, see Make it installable.

Turn it on or off

The PWA is a switchable feature, id pwa, and the only one that is off by default, so a plain website gets no service worker. It needs no other feature. Turn it on in src/custom/features.ts:

export const CUSTOM_FEATURES: FeatureChoice = {
    on: ['pwa'],
};

The choice is read at build time, so it takes a new build and deploy.

When it is off, the build writes no manifest, icons or service worker, the install card and update bar never show, the dashboard has no App installs numbers, and the trackPwaEvent function is not built. To turn it off again, remove 'pwa' from on and deploy: on each visitor's next visit the site removes its service worker and stored files, so everyone gets the plain website. An icon already on a home screen still opens the site, as a normal web page.

Set it up

  1. Name it in src/custom/pwa.ts. Anything you leave out keeps the default:

    export const CUSTOM_PWA: Partial<PwaConfig> = {
        name: 'Sanskrit for Kids',
        shortName: 'Sanskrit',
        description: 'Learn Sanskrit through play.',
        themeColor: '#e8590c',
        backgroundColor: '#ffffff',
        startUrl: '/learn',
    };
    KeyDefaultUsed for
    nameArc CMSThe install dialog and the app switcher.
    shortNameArc CMSUnder the home-screen icon. Keep it to about 12 characters. Set it whenever you set name: left out, it stays Arc CMS.
    descriptionEmptyThe manifest's description.
    themeColor#1d47a3The browser bar and phone status bar, and the install card's accent.
    backgroundColor#ffffffThe splash screen while the app opens.
    startUrl/The page the home-screen icon opens.
    navigationTimeoutSeconds4How long a page waits for the network before the stored copy opens, from 1 to 30 seconds. Lower it for an app often on a network without internet, such as a kiosk on a closed Wi-Fi: each start then waits less. Too low, and a slow connection gets the stored copy instead of fresh content.
    iconsrc/custom/pwa-icon.svg or .png, else Arc CMS'sThe image every icon size is made from, an .svg or .png named by its path from the project folder, such as src/custom/brands/acme/icon.svg. For an app with a different icon per brand. A file that is not there stops the build.
    appleIconpaddedThe iPhone and iPad home-screen icon. padded puts your image on a white tile with a margin, which suits a logo on a transparent background. fill makes it fill its square with no margin, for an icon that is already a full coloured square (otherwise it shows as a smaller square on white). Transparent corners then show backgroundColor.
    routeCodevisitedWhich screens' code is stored when the app installs or updates: visited (only what every page needs, the rest as it is first opened), app (every screen outside the admin area) or all. See Make it installable.

    The defaults are in src/app/core/pwa/pwa-config.ts. The installed app opens in its own window without the browser bar (standalone), with the whole site in scope.

  2. Add one square icon, at least 512 by 512 pixels, as src/custom/pwa-icon.svg or src/custom/pwa-icon.png (the SVG wins if both exist), or anywhere in the project with icon naming it. Every size is made from it at build time, including the iPhone icon and the "maskable" one Android crops to a circle or rounded square, so keep the important part within the middle 80%. Without one, a plain "Arc" icon is used. The site's favicon is not changed.

  3. Build and deploy (see Deploy). Deploy the functions too, for the install numbers.

Do not put enabled in src/custom/pwa.ts. The switch lives in src/custom/features.ts, and the build stops with a message saying so.

Use it

The install card

<arc-install-prompt /> shows what fits each browser:

BrowserShown
Chrome and Edge on Android and computersAn Install button, which opens the browser's own dialog.
Safari on iPhone and iPadTwo steps: tap Share, then "Add to Home Screen".
Chrome, Edge, Firefox and other browsers on iPhone and iPad (iOS 16.4 and later)The same two steps, from that browser's own Share button: "Tap the Share button in your browser." (Chrome and Edge have it in the address bar.)
A web view inside another app (Instagram, Facebook, the Google app), or an older iOS"To install, open this page in Safari." with Copy link.
Already installed, or a browser that cannot installNothing.

Not now hides it on that device for 30 days. Arc CMS's own member dashboard (/user/dashboard) shows it at the top; an app that replaces that page places it itself.

The card carries its own styles and icons (inline SVG, no Bootstrap or Font Awesome classes), so it looks the same inside a Shadow DOM host. Its accent is themeColor.

iPhone and iPad read the home-screen icon and name from the page itself, not the manifest, so the build also adds an apple-touch-icon link and the apple-mobile-web-app-title (the short name) to every page. By default that icon is your image shrunk onto a white tile; with appleIcon: 'fill' it fills its square instead (see the table above).

Install numbers

With the PWA on, the admin dashboard shows App installs: installs in the last 30 days by platform (Android, iPhone and iPad, computer), the install rate (people whose record says they installed, out of everyone with a record), and openings from the home screen in the last 30 days (each device once a day).

The browser reports four events to the trackPwaEvent function: prompt_shown, installed, dismissed and opened_installed. prompt_shown and installed count once per device, opened_installed once a day per device, and dismissed each time someone declines. iPhone has no install event, so the first opening from the home screen counts as the install. Visitors who are not signed in are counted too, since people often install before signing in.

Test it

A service worker runs only on https or localhost, and only in a production build: npm run dev has none. To try it on your computer, build and serve the browser build:

npm run build
npx vite preview --port 5190 --outDir dist/client

Open http://localhost:5190. In Chrome's DevTools, Application: Manifest shows the name and icons, Service workers shows sw.js running, and the install icon appears in the address bar. On a phone, the site needs a real https address: deploy it and open it there.

Extend it

What it stores

WhereWhat
PwaStats/{YYYY-MM-DD}One document per day (UTC): each event's total and count per platform.
users/{docId}, field pwa{ installed, platform, installedAt, lastOpenedAt } for a signed-in person who installed. Written only when something changes: the first install, or the first opening of a day.
On the deviceThe service worker's stored files (caches named arc-... and workbox-...) and, in local storage, the "Not now" date and which events were already sent.

Cloud Function: trackPwaEvent (callable, built only with the feature). One caller counts at most 30 events an hour; the rest are accepted and not counted. The build writes sw.js, the manifest and the icons into the site; no Storage files are used.

Who can do what

Troubleshooting

SymptomCause and fix
No install option on npm run devThe dev server has no service worker. Test a production build, as above.
The card asks someone on iPhone to open the page in SafariThey opened it inside another app (Instagram, Facebook, the Google app) or on iOS older than 16.4, where only Safari adds to the home screen. Safari, Chrome, Edge and Firefox on a current iPhone get the Share steps.
The iPhone icon is a small square on a white tileThe default appleIcon: 'padded'. For an icon that is already a full square, set appleIcon: 'fill' in src/custom/pwa.ts, then build and deploy. A home screen already holding the old icon keeps it until the app is added again.
The install card never shows againSomeone pressed Not now on that device less than 30 days ago.
A change to the name, colors or icon does not showThey are read at build time. Build and deploy again; an installed app picks them up after its update.
Users were not asked to update after a deployNothing in the app's files changed, so there is no new version. Content published in the CMS needs no update.
App installs shows nothingThe trackPwaEvent function is not deployed, or nobody has installed yet.