File System Access & OPFS
Two ways to write files
Section titled “Two ways to write files”The browser provides two distinct file-writing surfaces:
- File System Access API (
showOpenFilePicker,showSaveFilePicker) — lets users pick real files from their local file system. Requires a user gesture (button click). Not available in all browsers. - Origin Private File System (OPFS) — a sandboxed, origin-scoped virtual file system. No user gesture, no file picker, fully programmatic. Works in workers. Quota-managed like IndexedDB.
File System Access API
Section titled “File System Access API”The picker functions show the native OS file dialog. They must be called from a user-gesture handler.
// Open a file chosen by the userasync function openFile() { // showOpenFilePicker must be called inside a user gesture (click, etc.) const [fileHandle] = await window.showOpenFilePicker(); const file = await fileHandle.getFile(); const text = await file.text(); console.log('File contents:', text.slice(0, 200));}
// Save / overwrite a fileasync function saveFile(content) { const handle = await window.showSaveFilePicker({ suggestedName: 'output.txt', types: [{ description: 'Text file', accept: { 'text/plain': ['.txt'] } }], }); const writable = await handle.createWritable(); await writable.write(content); await writable.close();}Because showOpenFilePicker and showSaveFilePicker require a user gesture, they cannot be run autonomously in the StorageRunner above. The OPFS demo below is fully runnable.
Origin Private File System (OPFS)
Section titled “Origin Private File System (OPFS)”OPFS gives every origin a private directory tree. You do not need a user gesture — just call navigator.storage.getDirectory().
// Get the root of the OPFSconst root = await navigator.storage.getDirectory();
// Create or open a file handleconst fileHandle = await root.getFileHandle('notes.txt', { create: true });
// Write to itconst writable = await fileHandle.createWritable();await writable.write('Hello from OPFS!');await writable.close();
// Read it backconst file = await fileHandle.getFile();const text = await file.text();console.log(text); // 'Hello from OPFS!'Directory navigation
Section titled “Directory navigation”const root = await navigator.storage.getDirectory();
// Create a sub-directoryconst subDir = await root.getDirectoryHandle('drafts', { create: true });
// List entriesfor await (const [name, handle] of subDir.entries()) { console.log(handle.kind, name);}