"Offline-first" used to mean caching a to-do list so a phone could edit it on the subway. In 2026 it means something bigger: entire compute-heavy apps — video encoders, SQL engines, OCR — that run in the browser with no network at all. This guide explains how, using Zro7 as a worked example.
What offline-first actually means
An offline-first app is one where the browser is the primary runtime and the network is optional. That's the inversion. In a traditional web app the server is the runtime and the browser is a display; if the server is unreachable, the app dies. In an offline-first app the browser has everything it needs — code, data, and compute — and the server, when present, is a sync layer.
The four APIs that make it possible
1. Service Workers
A service worker is a background script the browser runs on your origin. It intercepts every network request the page makes and can return a cached response instead. That's how a Zro7 tool page keeps working with the network off — the HTML, JS, CSS, and WebAssembly binaries are already in the Cache Storage from the first visit.
2. Cache Storage & IndexedDB
Cache Storage holds HTTP responses (perfect for static assets and .wasm files). IndexedDB is a structured, transactional key-value store good for user data — a whiteboard's shapes, a podcast's edited waveform, a session's OCR outputs.
3. WebAssembly
The reason "offline-first" now covers compute-heavy work. FFmpeg, DuckDB, SQLite, Tesseract, ImageMagick, OpenCascade — every one of them has been compiled to WebAssembly. They run at close to native speed inside the browser sandbox, on the CPU cores your laptop already has.
4. File System Access API
The final piece. It lets a page read and write real files on your disk with your permission, no upload. That's what makes Big CSV Viewer possible — DuckDB-WASM streams a 4 GB CSV directly from the file handle instead of loading it into memory.
A real example: Zro7 Compress Video
Here's what happens when you drag an MP4 onto Compress Video:
- The service worker returns the tool's HTML + JS from Cache Storage — no network hit.
- The page requests
ffmpeg-core.wasm(~30 MB). If it's the first visit it's downloaded once; every visit after that it's a cache hit. - You drop the video. The File System Access API gives the page a handle — no upload.
- FFmpeg.wasm runs inside a Web Worker so the UI stays responsive.
- The compressed output is written to a Blob URL that turns into a download link.
- Nothing about that pipeline requires a server to be reachable.
The three hard problems, and how to solve them
Problem 1: The first-visit download
A WebAssembly video encoder is 20–40 MB. Solution: split it (ffmpeg-core loads separately from the app shell), compress it with Brotli, and cache it aggressively. After the first visit, subsequent loads are effectively free.
Problem 2: Big files vs limited RAM
You can't load a 4 GB CSV into JavaScript memory. Solution: stream. DuckDB-WASM reads from a FileSystemFileHandle in chunks. FFmpeg.wasm processes video in frames, not all at once.
Problem 3: Long tasks blocking the UI
A compression run can take a minute. Solution: run every WASM module in a Web Worker so the main thread stays responsive. Report progress with postMessage.
When offline-first is the wrong choice
- Anything that needs real-time collaboration between multiple users.
- Workloads whose data is genuinely too large to hold on one device (an analytics warehouse).
- Models too large to ship to a browser — a 70B-parameter LLM won't fit.
For most file-manipulation tasks — the ones people used cloud converters for — offline-first has become the better default.
Try it yourself
Open Compress Image in a new tab, wait for it to fully load, disable your Wi-Fi, and drop a photo. It works. That's the whole guide, in ten seconds.
Zro7