Examples menu

๐Ÿงต Background Upload via SharedWorker

A demonstration of how SharedWorker + extendedLifetime (Chrome 148+) lets an MPA behave like an SPA โ€” background work survives page navigations without a single line of SPA routing code.

The core idea: in a classic MPA, navigating away tears down the page, kills any in-flight XHR, and resets all client state. A SharedWorker partially breaks that rule โ€” it is shared across tabs and not tied to a single page lifecycle. But the browser is still allowed to terminate it the moment all ports disconnect, which happens briefly during every navigation.
extendedLifetime (Chrome 148+) closes the last gap: without it, the browser may terminate the SharedWorker during the brief moment between pages when the port count drops to zero. On Chrome 148+, a real upload continues chunking with zero user friction. On other browsers, that gap can kill the worker โ€” hence the navigation guard.
SESSION-scoped signals are the server-side complement. They hold uploadStatus, uploadPct, uploadFileName, and byte counters across all tabs. When the new page's SSE stream opens, the server immediately pushes the current state โ€” no polling, no local storage.
Real file mode shows the full picture: the worker slices the file into 512 KB chunks (File.slice()) and POSTs them one by one to the uploadChunk action. Between each await the worker may receive a connect message with fresh URLs from the new page, making mid-upload navigation transparent.
Graceful fallback: on browsers where the worker can be killed between pages, an in-page confirm dialog intercepts subnav clicks and a beforeunload handler covers other navigation. The guard is automatically suppressed on Chrome 148+ once the worker's workerBorn timestamp confirms it survived.

Account Settings

This is a placeholder page to demonstrate that uploads survive navigation. The progress bar above is still tracking your upload โ€” driven by SESSION-scoped signals.

Profile
Upload preferences

โšก Signals

uploadStatus string SESSION "idle"

Upload lifecycle: idle | uploading | complete | cancelled.

uploadPct int SESSION 0

Upload progress 0โ€“100. Authoritative server value โ€” navigation gaps self-heal on next chunk.

uploadFileName string SESSION ""

Filename displayed in the progress bar.

uploadTotalBytes int SESSION 0

Total file size in bytes (virtual or real).

uploadedBytes int SESSION 0

Bytes transferred so far.

uploadFileInfo string SESSION ""

Human-readable file metadata for real uploads, e.g. "3.2 MB ยท PDF". Empty for simulated uploads.

uploadMode string TAB "sim"

Which form is shown: sim (SharedWorker simulation) or real (multipart upload). TAB-scoped โ€” each tab can differ.

๐ŸŽฏ Actions

startUpload SESSION

Initialises upload SESSION signals (status=uploading, total, filename) and broadcasts to all tabs. Called by the worker before the chunk loop.

receiveChunk SESSION

Simulated mode only. Called by SharedWorker every 200\u202fms. Updates pct\u202f+\u202fuploadedBytes using authoritative worker value (self-heals navigation gaps), broadcasts.

uploadChunk SESSION

Real file mode only. Receives a 512\u202fKB slice from the SharedWorker chunk loop. Tracks offset\u202f+\u202fsize, updates pct/uploadedBytes, sets status=complete and formats uploadFileInfo on the final chunk.

setMode TAB

Switches uploadMode between sim and real, clears file errors, re-renders the form block.

cancelUpload SESSION

Resets all upload signals to idle. Works from any sub-page.

resetUpload SESSION

Clears completed/cancelled state so a new upload can begin.

๐Ÿ‘ Views

file-upload.html.twig

Mode toggle, sim form (filename + size/speed pickers), real file form. Real uploads use File.slice() chunks (512\u202fKB each) sent by the SharedWorker via uploadChunk. Navigation guard shown on non-Chrome browsers.

file-upload-browse.html.twig

Fake file browser sub-page showing live upload row during transfer.

file-upload-settings.html.twig

Fake settings sub-page proving upload state survives any navigation.