expo-opfs
v1.0.5
Published
[OPFS](https://developer.mozilla.org/en-US/docs/Web/API/File_System_API/Origin_private_file_system) polyfill for expo based on the [Expo Filesystem API](https://docs.expo.dev/versions/latest/sdk/filesystem/).
Readme
expo-opfs
OPFS polyfill for expo based on the Expo Filesystem API.
If you're building an Expo app and want to use a web library that needs the Origin Private File System (OPFS), this polyfill based on expo-filesystem makes it work out of the box.
Created for the Expo RxStorage of RxDB.
Usage
You can use this module either by applying a polyfill to the global navigator environment or by importing the opfs api object directly.
1. Polyfill the Global Environment
To polyfill navigator.storage.getDirectory in your Expo application, import the package and call applyPolyfill() at your project's entry point (e.g., App.js or index.js).
import { applyPolyfill } from 'expo-opfs';
applyPolyfill();
// Now you can safely use the standard OPFS API:
async function writeData() {
const root = await navigator.storage.getDirectory();
const fileHandle = await root.getFileHandle('my-file.txt', { create: true });
const writable = await fileHandle.createWritable();
await writable.write('Hello, world!');
await writable.close();
}2. Import the OPFS Object Directly
If you prefer not to modify the global navigator environment, you can import the opfs instance directly from the package:
import { opfs } from 'expo-opfs';
async function writeData() {
const root = await opfs.getDirectory();
const fileHandle = await root.getFileHandle('my-file.txt', { create: true });
const writable = await fileHandle.createWritable();
await writable.write('Hello, world!');
await writable.close();
}Synchronous API
This polyfill fully implements the FileSystemSyncAccessHandle interface, bringing high-performance fully synchronous read(), write(), getSize(), truncate(), and flush() methods to React Native.
This is powered by Expo SDK 55's modern FileSystem native C++ JSI bindings, making it possible to run complex WebAssembly (WASM) applications like SQLite natively on the device using standard OPFS adapter libraries.
TODO Update expo sdk to version 55 to remove this note
Performance
expo-opfs is strictly optimized to align with native OPFS web performance where possible.
| Benchmark | Browser OPFS (Chromium 145.0.7632.116) | Expo OPFS (Google Pixel 9) | | --- | --- | --- | | Sequential small writes (500 files) | ~763ms | ~969ms | | Sequential reads (500 files) | ~365ms | ~350ms | | Directory Create/Delete (500 ops) | ~223ms | ~702ms | | Random access writes (50 ops on 5MB file) | ~1060ms | ~3291ms | | Random access reads (500 slice reads on 5MB file) | ~235ms | ~49ms | | SyncAccessHandle writes (1000 ops) | - | ~9ms |
Testing
The implementation is validated against the exact same test suites inside both a raw Node.js/JSDOM simulator and a native physical smartphone environment simultaneously via a unified Harness.
- Browser Automated Suite: Run
npm run test:browserto execute native mock interactions instantly through Jest across a Chromium browser environment. - Expo Node Simulator: Run
npm run test:expoto execute the test suite natively under Expo's Jest preset simulating the React Native bridge. - On-Device Target Simulation: Run
npm run test:exampleto launch a fully configured minimalApp.tsxcontainer on your physical development device through Expo Go. The App transparently mounts the exact identical OPFS compliance benchmarks executed insidenpm run test, natively resolving them asynchronously entirely outside the simulated Jest context, and logs the unified output straight to your screen!
