The essentials

Quick reference

One focused task per row. Jump to the related section for complete, working examples.

UseSyntaxExamples
Choose filesconst [handle] = await showOpenFilePicker({ multiple: false, types })View examples
Choose a save destinationconst handle = await showSaveFilePicker({ suggestedName: 'report.txt', types })View examples
Choose a directoryconst directory = await showDirectoryPicker({ mode: 'readwrite' })View examples
Query handle permissionconst state = await handle.queryPermission({ mode: 'readwrite' })View examples
Request handle permissionconst state = await handle.requestPermission({ mode: 'readwrite' })View examples
Read a file snapshotconst file = await handle.getFile(); const text = await file.text()View examples
Open a writable streamconst writable = await handle.createWritable({ keepExistingData: true })View examples
Write and commitawait writable.write(contents); await writable.close()View examples
Get or create a child fileconst file = await directory.getFileHandle('notes.txt', { create: true })View examples
Iterate directory entriesfor await (const [name, handle] of directory.entries()) inspect(name, handle)View examples
Remove a directory entryawait directory.removeEntry('cache', { recursive: true })View examples
Open the OPFS rootconst root = await navigator.storage.getDirectory()View examples
Open worker synchronous accessconst access = await fileHandle.createSyncAccessHandle()View examples
Estimate storage usageconst { usage, quota } = await navigator.storage.estimate()View examples
Request persistent storageconst persistent = await navigator.storage.persist()View examples

The File System API exposes two different surfaces: picker-acquired handles to user-visible files where supported, and an origin-private filesystem reached through navigator.storage. Picker APIs require a secure top-level context, transient activation, and explicit user selection. OPFS is private, quota-managed application storage—not a path into the user's filesystem. Feature-test every capability and design for revocation, external edits, quota failure, and data deletion.

Step by step

Detailed examples

01

Open pickers only from explicit user actions

showOpenFilePicker(), showSaveFilePicker(), and showDirectoryPicker() are window methods for secure contexts and require transient user activation. Browser support varies, and pickers may reject with AbortError when canceled or SecurityError when disallowed. MIME types and extensions are hints, not content validation. Never upload, overwrite, or recursively scan immediately after selection without showing scope and obtaining the appropriate user intent. Provide input type=file and download fallbacks.

Choose one text file with cancellation handling
async function chooseTextFile() {
  if (!window.showOpenFilePicker) return chooseWithFileInput();
  try {
    const [handle] = await showOpenFilePicker({
      types: [{ description: 'Text', accept: { 'text/plain': ['.txt', '.md'] } }],
    });
    return handle;
  } catch (error) {
    if (error.name === 'AbortError') return null;
    throw error;
  }
}
Back to quick reference ↑
02

Recheck handle permission at the moment of use

A handle may remain structured-clonable and persistable in IndexedDB while its effective permission returns to prompt or denied. Read and readwrite are separate modes. queryPermission() does not prompt; requestPermission() may require a fresh activation and is not available uniformly. Store handles only after explaining the convenience, remove stale records, and never treat possession of a serialized handle as authorization. The user or platform can revoke access outside the app.

Require write permission before an edit
async function requireWrite(handle) {
  const options = { mode: 'readwrite' };
  if (await handle.queryPermission?.(options) === 'granted') return true;
  if (!handle.requestPermission) return false;
  return await handle.requestPermission(options) === 'granted';
}
Back to quick reference ↑
03

Read fresh snapshots and commit writable streams

getFile() returns a File snapshot whose data can become unreadable after the disk file changes; request another snapshot to refresh. createWritable() typically writes through a temporary file and commits on close, but this does not provide multi-process transaction isolation. keepExistingData false permits replacing content efficiently; true is needed for seek and partial editing. Always await close(), call abort() on failure where available, handle quota and permission errors, and coordinate same-app writers.

Replace a file and commit explicitly
async function saveText(handle, text) {
  if (!await requireWrite(handle)) throw new DOMException('Write access denied', 'NotAllowedError');
  const writable = await handle.createWritable();
  try {
    await writable.write(new TextEncoder().encode(text));
    await writable.close();
  } catch (error) {
    await writable.abort?.(error).catch(() => {});
    throw error;
  }
}
Back to quick reference ↑
04

Traverse selected directories with explicit bounds

Directory handles provide async values(), keys(), and entries(), plus getFileHandle(), getDirectoryHandle(), removeEntry(), and resolve(). Child names are single path components; reject user-generated separators and dot segments rather than pretending they are paths. Iteration can be large and entries can change while scanning, so yield progress, cap depth and count, and allow cancellation. Recursive deletion is irreversible from the API perspective and needs confirmation specific to the selected subtree.

List a bounded set of direct children
async function listChildren(directory, limit = 500) {
  const children = [];
  for await (const [name, handle] of directory.entries()) {
    children.push({ name, kind: handle.kind });
    if (children.length >= limit) break;
  }
  return children;
}
Back to quick reference ↑
05

Use OPFS for app-private data and sync access only in workers

navigator.storage.getDirectory() returns the origin-private root of the current storage bucket. OPFS filenames are not user-visible paths and data can be cleared with site data or evicted unless storage persistence policy protects it. createSyncAccessHandle() is for OPFS files in dedicated workers and generally grants exclusive access to that file until close; it enables synchronous read, write, truncate, getSize, and flush without blocking the page. Always close it in finally.

Use a synchronous OPFS handle inside a dedicated worker
const root = await navigator.storage.getDirectory();
const file = await root.getFileHandle('database.bin', { create: true });
const access = await file.createSyncAccessHandle();
try {
  const bytes = new TextEncoder().encode('CMDM');
  access.write(bytes, { at: 0 });
  access.truncate(bytes.length);
  access.flush();
} finally { access.close(); }
Back to quick reference ↑
06

Plan for quota, eviction, deletion, and origin isolation

estimate() reports approximate usage and quota and is not a promise that a future write will succeed. persist() requests reduced eviction risk, but the browser decides and users can still clear data. OPFS is isolated by origin and storage partitioning, not encrypted against same-origin script, browser extensions, malware, or device access. Validate content after reading, encrypt only through a reviewed key-management design, expose data export and deletion, and handle QuotaExceededError without destructive retry loops.

Check headroom without treating it as reserved
async function storageStatus() {
  const { usage = 0, quota = 0 } = await navigator.storage.estimate();
  const persistent = await navigator.storage.persisted?.() ?? false;
  return { usage, quota, availableEstimate: Math.max(0, quota - usage), persistent };
}
Back to quick reference ↑

Sources and further reading

References

Authoritative documentation used to verify and expand this cheat sheet.

  1. WHATWGFile System Standardfs.spec.whatwg.org
  2. Web Applications Working GroupFile System Access: Editors' Draftwicg.github.io
  3. WHATWGStorage Standardstorage.spec.whatwg.org
  4. WHATWGHTML Standard: File Upload Statehtml.spec.whatwg.org
  5. WHATWGWeb IDL Standard: Serializable Objectswebidl.spec.whatwg.org

Help us improve

Found a typo or missing example?

Tell us what would make this cheat sheet clearer, more complete, or more useful.

Share feedback