lunx.docs

Progressive web apps

Built into lunx: a web app manifest and a service worker that precaches your build, so the app installs and works offline. No plugin, no Workbox.

Setup

typescriptlunx.config.ts
import { defineConfig } from 'lunx-dev'

export default defineConfig({
  pwa: {
    manifest: {
      name: 'My Lunx App',
      short_name: 'LunxApp',
      theme_color: '#2563eb',
      background_color: '#ffffff',
      icons: [
        { src: '/icons/pwa-192x192.png', sizes: '192x192', type: 'image/png' },
        { src: '/icons/pwa-512x512.png', sizes: '512x512', type: 'image/png' },
        { src: '/icons/pwa-512x512.png', sizes: '512x512', type: 'image/png', purpose: 'maskable' },
      ],
    },
  },
})

manifest is written as-is (keys as in the Web App Manifest spec), with start_url, scope and display: 'standalone' filled in when you leave them out. Put the icons in public/.

What lunx build writes

text
dist/
├── manifest.webmanifest   # from pwa.manifest
├── registerSW.js          # registers sw.js once the page has loaded
├── sw.js                  # precaches the build
└── index.html             # + <link rel="manifest"> and <script src="/registerSW.js" defer>
  • Precache: every file of the finished build (pages with their SRI, assets, public/), except source maps, .gz / .brcopies and lunx's reports.
  • Cache-first: precached files load from the cache; anything else goes to the network.
  • Offline navigation: a page that isn't cached (a client-side route) gets index.html, so single-page apps keep working.
  • Updates: each build has its own cache. A deploy installs the new service worker, which takes over and deletes the old cache.
  • registerSW.js is an external script, so lunx's Content-Security-Policy (script-src 'self') allows it.

lunx dev registers no service worker: it would serve your edits from a stale cache.

Options

FieldTypeDefaultDescription
manifestobject | false—The web app manifest, or false to write none.
registerbooleantrueAdd registerSW.js to every page. false to register the service worker yourself.
navigateFallbackstring | null'index.html'The page offline navigations fall back to; null for none (multi-page sites).
exclude(string | RegExp)[]—Build files to leave out of the precache. Strings may use * wildcards.

pwa: true turns it on with the defaults.

Checking it

bash
lunx build && lunx preview
# DevTools → Application → Service Workers: sw.js is "activated"
# DevTools → Network → Offline, then reload: the app still loads
Already using vite-plugin-pwa?
It keeps working with lunx, and while it is in your plugins lunx writes no service worker of its own.
Maskable icons
Provide a maskable icon variant. Android crops icons to various shapes; without a maskable variant, the standard icon is padded with white space.