@componentor/fs
MIT · TypeScript · 134/134 node:fs

Node's fs, in the browser. Synchronous.

A readFileSync that actually blocks and returns a value. The sync methods are real, bridged to OPFS with SharedArrayBuffer and Atomics.wait. Your files survive a reload.

npm install @componentor/fs Try it in your browser Read the docs
import { VFSFileSystem } from '@componentor/fs';

const fs = new VFSFileSystem({ root: '/my-app' });
await fs.init();                          // mount before the first blocking call

fs.writeFileSync('/hello.txt', 'Hello from the browser');
fs.readFileSync('/hello.txt', 'utf8');    // → 'Hello from the browser'

Run Node code that was never written for an async filesystem

A real sync API

Blocking readFileSync, writeFileSync and the rest. Not callbacks pretending to be sync.

All of node:fs

All 134 functions across node:fs and node:fs/promises on Node 24: streams, file descriptors, watch, glob, cp.

Persistent

A compact binary VFS in OPFS, with an optional mirror to real OPFS files that DevTools can see.

Safe across tabs

Every tab shares one filesystem. One tab is elected leader, with automatic failover via navigator.locks. Works on Chrome, Safari and Firefox.

Zero bundler config

Worker bundles are embedded, so there is nothing to copy or host. Works with Vite, a CDN, or a plain <script type="module">.

Async works everywhere

fs.promises needs no special headers. isomorphic-git runs on it.

One thing to know first

The synchronous API needs a cross-origin isolated page: Cross-Origin-Opener-Policy: same-origin and Cross-Origin-Embedder-Policy: require-corp. If you can't set headers, the async API is fully supported.

Questions

Does readFileSync really block in the browser?

Yes. The *Sync methods block the calling thread with SharedArrayBuffer and Atomics.wait while a worker does the OPFS I/O, and then return the value. This needs a cross-origin isolated page (COOP and COEP headers). Without those headers, the async API still works.

Where are the files stored?

In the browser's Origin Private File System (OPFS), as a compact binary volume, so they survive a reload. An optional mirror also writes real OPFS files that DevTools and other tools can see.

Which browsers are supported?

Chrome, Safari and Firefox, including several tabs sharing one filesystem. The docs list the one case that stays impossible: a Safari follower tab making sync calls from the main thread rather than a worker.

Do I need to configure my bundler?

No. The worker bundles are embedded in the package, so there is nothing to copy or host. It works with Vite, from a CDN, or in a plain script tag of type module.

What is the difference between @componentor/fs and sync-opfs?

None. They are the same package published under two names.