Skip to content

Progressive Web App (PWA)

Loquent ships as a Progressive Web App. Users can install it to their home screen on Android and iOS, and get a standalone app experience without browser chrome.

The PWA implementation adds four components:

  • Web app manifest — defines app identity, icons, and display mode
  • Service worker — caches assets with routing-aware strategies
  • Offline fallback page — shown when the network is unavailable
  • HTML meta tags — configure theme color, viewport, and iOS integration

The manifest at public/manifest.json tells the browser how to install and display the app:

{
"name": "Loquent",
"short_name": "Loquent",
"display": "standalone",
"theme_color": "#f59e0b",
"background_color": "#fafafa",
"start_url": "/",
"scope": "/",
"icons": [
{ "src": "/icons/icon-192x192.png", "sizes": "192x192", "type": "image/png" },
{ "src": "/icons/icon-512x512.png", "sizes": "512x512", "type": "image/png" },
{ "src": "/icons/icon-512x512.png", "sizes": "512x512", "type": "image/png", "purpose": "maskable" }
]
}

Setting display to standalone removes browser chrome (address bar, tabs) when the app is launched from the home screen.

The service worker at public/sw.js intercepts all GET requests and applies different caching strategies based on the request type:

Request PatternStrategyWhy
/api/*Network-onlyAPI data is authenticated and dynamic
/auth/*, /login, /signupNetwork-firstAuth must be fresh, cache as fallback
Hashed assets (-[hash].js/wasm/css)Cache-firstContent-hashed files are immutable
Static assets (images, fonts)Stale-while-revalidateInstant response, silent background update
Navigation requestsNetwork-first + offline fallbackFalls back to /offline.html

Caches are namespaced with a version prefix:

var CACHE_VERSION = "v1.0.0";
var STATIC_CACHE = CACHE_VERSION + "::static";
var PAGES_CACHE = CACHE_VERSION + "::pages";

When you bump CACHE_VERSION, the service worker’s activate event deletes all caches from previous versions automatically.

The service worker registers via an inline script in src/bases/client/client_app.rs:

const SW_REGISTER_SCRIPT: &str = r#"if("serviceWorker"in navigator){
navigator.serviceWorker.register("/sw.js",{scope:"/"})
.catch(function(e){console.warn("SW registration failed:",e)})
}"#;

This follows the same pattern as THEME_INIT_SCRIPT — inline JavaScript rendered in the HTML head.

When a navigation request fails (no network and no cache), the service worker serves public/offline.html. This page:

  • Displays a “You’re offline” message with a retry button
  • Supports dark and light mode via prefers-color-scheme
  • Uses Loquent’s amber accent (#f59e0b) for branding
  • Is self-contained — no external dependencies

Five meta tags in client_app.rs handle mobile integration:

Meta { name: "viewport", content: "width=device-width, initial-scale=1, viewport-fit=cover" }
Meta { name: "theme-color", content: "#f59e0b" }
Meta { name: "apple-mobile-web-app-capable", content: "yes" }
Meta { name: "apple-mobile-web-app-status-bar-style", content: "default" }
Meta { name: "apple-mobile-web-app-title", content: "Loquent" }

The viewport-fit=cover value ensures the app fills the screen on devices with notches.

Two changes in Dioxus.toml make PWA assets available:

[application]
asset_dir = "public"
[web.watcher]
watch_path = ["src", "public"]

Without asset_dir = "public", the manifest, service worker, and icons would 404.

The icon set lives in public/icons/:

FileSizeUse
icon.svgSourceAmber gradient with white “L” lettermark
icon-192x192.png192×192Android home screen
icon-512x512.png512×512Android splash screen, high-DPI
apple-touch-icon.png180×180iOS home screen
  1. Run dx serve and open Chrome DevTools
  2. Go to Application → Manifest — verify app name, display mode, and icons
  3. Go to Application → Service Workers — confirm sw.js is registered
  4. Check Application → Cache Storagev1.0.0::static should contain offline.html
  5. Toggle Offline in the Service Workers panel, then navigate — the offline page should appear