Lesson 0001 · ~10 minutes

When OPFS fits (and when it doesn’t)

One skill: place the Origin Private File System on the client storage spectrum and pick it only when the hard problem is file-shaped bytes private to the origin (editors, media pipelines, large blobs treated as files).

Win for this lesson

After the quiz, you can defend “OPFS yes / no” in a design review without writing a line of getDirectory yet — and without confusing it with IndexedDB, Cache Storage, or user-visible file pickers.

1. The problem OPFS solves

Some product data is not “a row with keys” and not “an HTTP response.” It is a file: a PDF export, a multi-MB media buffer, a SQLite database file, a directory of project assets. You want create / read / write / delete, nested folders, and eventually high-throughput byte I/O — still inside the browser’s security sandbox.

The Origin Private File System (OPFS) is the browser’s main answer for that shape: an origin-scoped virtual filesystem that is not visible in Finder/Explorer. You enter it with navigator.storage.getDirectory(), which returns a root FileSystemDirectoryHandle. web.dev ’s default split is blunt: for network resources needed to load the app, use Cache Storage; for file-based content, use OPFS; for other app data, use IndexedDB.

OPFS is available from windows, workers, and service workers. A sync access handle path exists for maximum throughput in dedicated workers (the reason Wasm databases love OPFS) — that depth comes later. Today only need the job description.

2. Not IndexedDB, not Cache API, not “the user’s Desktop”

Three common confusions:

Mental model: OPFS is a private disk folder for your origin that only your site’s code can see. Users do not browse it; they experience features built on top of it (exports, offline media, local engines).

3. The client storage spectrum (OPFS highlighted)

Same map you used in IndexedDB and Cache API — steal again from Storage for the web. Today the spotlight is the OPFS row.

Memory only

React/Vue state, module variables. Fastest. Dies on refresh. Fine when re-fetch or re-derive is cheap.

sessionStorage

Tab-scoped string map; survives reloads in that tab, not other tabs. Sync API; tiny; not for workers. Prefer sparingly.

localStorage

Origin-scoped string map that persists. Sync on main thread; ~few MB; no workers. Avoid for bulk or hot-path structured data.

Cookies

Tiny values often sent on every HTTP request. Session/auth use-cases only — not a general client database or file store.

IndexedDB

Large structured app data: drafts, entity caches, offline queues, user-generated records. Async; keys + indexes; works off-main-thread.

Cache API

Request/Response pairs — HTML/JS/CSS/images and other HTTP resources for load/offline shells and runtime resource caching.

OPFS

File-shaped bytes on an origin-private filesystem. Editors, media pipelines, “this is a file” models, high-throughput worker I/O — not the first stop for HTTP shells or keyed records.

Default rule (steal this)

If the hard part is “store and manipulate file-shaped content private to this origin”, start with OPFS. If the hard part is “persist structured application data so we can show or edit records without a round trip”, that is IndexedDB. If the hard part is “make the app shell and static assets load offline”, that is the Cache API (usually with a service worker). If the hard part is “open/save a file the user can see in Finder”, that is File System Access pickers, not OPFS. If the hard part is “truth lives on the server and re-fetch is fine”, prefer no durable client store.

Signal Leans OPFS Leans away
Shape Files, folders, large blobs, byte ranges Keyed objects, HTTP Request/Response pairs
Job Local file pipelines; high-throughput I/O Query/filter entities; offline app shell
Visibility Private to origin; no OS explorer User must pick a real path on disk
Classic fits Offline media cache-as-files, export packages, Wasm/SQLite DB file, project asset trees Multi-paragraph draft rows, CRM contacts, theme toggle, offline app.js shell
FE integration difficulty (honest preview)

Opening the root directory is not the hard part. What bites teams later: quota / eviction (OPFS is still best-effort storage unless you request persistence), main-thread vs worker (sync access handles are for dedicated workers), debugging (DevTools OPFS support is uneven), and assuming private files are “safe forever” or multi-device truth. We’ll drill those later. Today only know they exist so you don’t oversell “just use OPFS.”

4. Practice — pick the store

Choose the best default. Equal-length options so formatting doesn’t hint. Feedback is immediate.

Scenario A

Podcast app: store multi-MB episode audio offline as files so a worker can stream bytes without re-downloading.

Scenario B

Writing app: multi-paragraph drafts must survive refresh and work offline until the user hits Publish.

Scenario C

PWA shell: HTML, CSS, and JS must load so the UI appears when the network is completely gone.

Scenario D

Desktop-class editor: user opens a real .docx from disk, edits, and saves back to the same path they chose.

Scenario E

Internal admin: always online; table of 20 rows from REST; no offline requirement; refresh is acceptable.

5. What to remember

Ask your teacher Anything fuzzy — “what about storing Blobs in IndexedDB instead?”, a feature on your team, Safari quirks you already hit — ask in chat. Follow-up questions are part of the method, not a distraction.

Primary source (read next)

web.dev — Storage for the web (spectrum and defaults). Then skim web.dev — The origin private file system so the next lesson’s handle API vocabulary isn’t new twice. MDN’s OPFS overview is the short normative companion.