Installing and offline use: your choice
The marketplace gives your app its own address and serves your files there. It does not require a manifest or a service worker, and it does not mark apps as "installable" or "works offline". If you want your app to be installable, to work offline, or to show a page that asks people to install it, you build that into your app. If you do, tell your users on your own page.
In the catalog, each app has an Open link that goes to its address. Nothing else.
Why each app gets its own subdomain
Every app runs on its own subdomain, which is its own browser origin. Your app has its own storage (IndexedDB, localStorage) and its own service worker, completely separate from every other app. One app can never read another app's data. This also means an install prompt belongs to your app alone.
If you want people to install it
Installing is a browser feature that needs two things from your files. Both are ordinary files in your bundle:
- a web manifest, linked from your pages with
<link rel="manifest" href="/manifest.webmanifest">, with a name,start_url,scope,display: "standalone"and icons (192 and 512 px); - a service worker (for example
sw.jsat the top level of the bundle, so its scope covers the whole app), registered from your code withnavigator.serviceWorker.register('/sw.js'). It is also what makes offline use possible.
Tools such as vite-plugin-pwa generate both for you. If your app has a manifest, the marketplace also uses its name, description, icon and tags for the listing. That is the only thing it reads from it.
A store page and the app in separate folders
A pattern that works well: put a store page at the root of your address that asks the visitor to install, and the app itself in a folder such as /app.
/shows the app's name, what it does and an Install button. The button keeps the browser'sbeforeinstallpromptevent and callsprompt()when pressed. Where a browser has no such event (iPhone and iPad Safari, for instance), the page explains how to install by hand.- The manifest sets
"start_url": "/app"and"scope": "/". The installed app always opens/app, so people who installed it never see the store page, and people who didn't are asked. - When the store page is opened inside the installed window (
matchMedia('(display-mode: standalone)')), send it straight to/app.
The repository's examples/market-pwa is a complete example with a real service worker, icons and a browser-tested install flow.
If you want it to work offline
A service worker caches your files so the app opens without a network. Calls to the platform's backend (/_dg/) always need a connection, so keep a local copy of what the user needs and never cache anything under /_dg/.
Shipping an update
The marketplace revalidates sw.js, index.html and the manifest on every load, so an approved version reaches browsers without cache-busting. Browsers check for a new service worker when the app launches or navigates, and at most once a day while it stays open. To notice an update in an installed app that is already running, ask the registration to check again when the app comes back to the foreground:
navigator.serviceWorker.ready.then((registration) => {
document.addEventListener('visibilitychange', () => {
if (document.visibilityState === 'visible' && navigator.onLine) void registration.update()
})
})
Change something in your service worker on every build (for example a cache name that includes a hash of your files) so browsers see a new version. Never cache sw.js itself, or updates will not be detected. If your app has no service worker, it gets the new files on the next reload because index.html is revalidated.
Test it before you submit
- Serve your build locally and open it in Chrome. In DevTools, the Application panel shows the manifest and the service worker, and Lighthouse reports whether the app is installable.
- Switch the network to Offline in DevTools and reload: the app should still open.
- Remember that storage is per origin, so data from your local test won't carry over to your marketplace address.
dg.run