Make it work offline as an installable app
Continues from the last build: A well-tested, well-instrumented Pulse survives every unit and e2e run in CI, but hand someone a train tunnel or a hotel wifi and the metrics dashboard just spins, because every screen assumes the network is always there.
Last rung you locked Pulse down with unit tests, component tests, e2e flows, and an a11y gate in CI, so you trust every green checkmark.
What you'll build
Pulse installs like a native app, boots instantly offline from a precached shell, serves last-known metrics and events from cache the moment the network drops, shows a branded offline page only for the handful of routes it genuinely cannot serve, and tells the user in-app when a new version is ready instead of relying on a silent background swap or a manual hard refresh.
See how we teach, before you sign up
You don't just get code dumped on you. Every starter file and every solution is explained line-by-line, in plain English. Here's one real file from this project:
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [
react(),
// TODO(rung 10): add VitePWA({ ... }) here with:
// - manifest: false (we ship public/manifest.webmanifest ourselves)
// - workbox.globPatterns for the precached app shell
// - workbox.runtimeCaching for GET /api/* reads (StaleWhileRevalidate)
// - workbox.navigateFallback for the offline route
// - registerType: 'prompt' so updates never swap silently
],
build: {
outDir: 'dist',
},
});
Reading this file
react(),The existing React plugin stays first; VitePWA is additive and goes in the same plugins array.manifest: false (we ship public/manifest.webmanifest ourselves)Two valid patterns exist: let the plugin generate the manifest inline, or point it at a static file. This starter picks the static file so the manifest is easy to inspect and lint on its own.workbox.runtimeCaching for GET /api/* reads (StaleWhileRevalidate)This is the exact strategy milestone 3 wires up, only GET reads, never the POST /api/segments write.registerType: 'prompt'The default 'autoUpdate' swaps the service worker silently in the background, which is exactly how teams end up needing a hard refresh to see a fix; 'prompt' is what milestone 5 depends on.
Entry point for the PWA plugin, empty of any offline config until this rung fills it in.
That's 1 of 7 explained code blocks in this single project.
The build, milestone by milestone
- 1
Install vite-plugin-pwa and ship an installable manifest
4 guided stepsWithout a valid manifest served over HTTPS with a registered service worker, the browser has nothing to base an install prompt on, Pulse stays a tab that disappears the moment the connection drops, exactly the pain this rung exists to fix.
- 2
Precache the app shell so the UI itself never needs the network
4 guided stepsIf only data is cached and the shell itself is fetched fresh every time, a dead connection means the React app never even boots, precaching the shell is what makes the rest of this rung possible.
- 3
Stale-while-revalidate the API reads, with real expiration
4 guided stepsA dashboard that spins until the network answers feels broken the moment a connection blips, stale-while-revalidate means the user always sees last-known numbers immediately, with a fresher copy swapping in a moment later if the network cooperates.
- 4
Build a branded offline fallback route
4 guided stepsAn unvisited deep link on a dead connection has nothing cached to fall back to, without a dedicated offline route the browser shows its own dinosaur-style error page, which instantly breaks the illusion that Pulse is an app rather than a website.
- 5
Prompt for updates instead of forcing a hard refresh
4 guided stepsThe default service worker lifecycle keeps a new version waiting in the background until every tab closes, teams without an explicit update prompt end up telling users to hard-refresh or close all tabs to see a fix, which nobody knows to do.
What's inside when you start
You'll walk away with
This is portfolio-grade. Build it free.
Sign up to unlock every milestone step-by-step, the code skeletons, full reference solutions, and checkable tasks, with your progress saved as you build.
Start building