API in Capabilities
File System Access API
Published Updated
The File System Access API lets a page open, edit, and save real files and folders on the user’s device: showOpenFilePicker(), showSaveFilePicker(), and showDirectoryPicker() return FileSystemFileHandle and FileSystemDirectoryHandle objects that stay valid after the picker closes, can be stored in IndexedDB, and write back to the same file through createWritable(). The handle interfaces themselves are defined in the WHATWG File System Standard and are shared with the Origin Private File System.
The pickers are Chromium-only: Chrome 86 and Edge 86 on desktop. Firefox 111 and Safari 15.2 implement the handle interfaces for OPFS but expose no picker, and Safari added createWritable() only in Safari 26 (BCD api.Window.showOpenFilePicker, api.FileSystemFileHandle.createWritable). queryPermission() and requestPermission() on handles are Chrome 86 only.
Syntax
Section titled “Syntax”window.showOpenFilePicker()window.showOpenFilePicker(options)
window.showSaveFilePicker()window.showSaveFilePicker(options)
window.showDirectoryPicker()window.showDirectoryPicker(options)showOpenFilePicker() returns a Promise<sequence<FileSystemFileHandle>> (one element unless multiple is true); showSaveFilePicker() a Promise<FileSystemFileHandle>; showDirectoryPicker() a Promise<FileSystemDirectoryHandle>. All three are [SecureContext] and require transient user activation. A save-picker handle comes back with read-write permission already granted, and showSaveFilePicker() empties the chosen file at the moment the promise resolves, not when you later close() the writable stream.
Parameters
Section titled “Parameters”Each picker takes one optional options dictionary. OpenFilePickerOptions and SaveFilePickerOptions extend the shared FilePickerOptions; DirectoryPickerOptions is separate.
| Member | Type | Required | Applies to | Description |
|---|---|---|---|---|
types |
sequence<FilePickerAcceptType> |
No | open, save | Filters the user can pick from. Each entry has a description (USVString, default "") and an accept record mapping a MIME type to one suffix or a list of suffixes, for example { "text/plain": [".txt", ".md"] }. |
excludeAcceptAllOption |
boolean |
No | open, save | Default false. When true, the “All files” filter is omitted; when types is empty it is added regardless. |
id |
DOMString |
No | all | Up to 32 characters of letters, digits, _ or -. The browser remembers the last directory used with this id per origin and starts there next time. |
startIn |
WellKnownDirectory or FileSystemHandle |
No | all | Where to start: one of "desktop", "documents", "downloads", "music", "pictures", "videos", or a handle from an earlier pick. A remembered id directory takes precedence. |
multiple |
boolean |
No | open | Default false. When true, the user may select any number of files. |
suggestedName |
USVString? |
No | save | Pre-fills the file name; the browser may sanitise or ignore a name it considers dangerous. |
mode |
FileSystemPermissionMode |
No | directory | "read" (default) or "readwrite". With "readwrite" the permission prompt covers writing, so the picked directory needs no second prompt. |
Suffixes in accept must start with ., must not end with ., must be at most 16 characters, and may contain only valid suffix code points.
Exceptions
Section titled “Exceptions”The pickers reject in the order below; the SecurityError and TypeError cases are checked before any dialog appears.
| Exception | Condition |
|---|---|
SecurityError |
The document has an opaque origin (a sandboxed iframe); or its origin differs from the top-level origin (a cross-origin iframe); or the window has no transient user activation. |
TypeError |
A key in accept is not a valid MIME type or carries parameters; a suffix breaks the rules above; types plus the all-files option leaves no filter at all; id is longer than 32 characters or contains other characters. |
AbortError |
The user dismissed the dialog without choosing; or the browser judged the selection too sensitive (system folders, the downloads directory as a whole) and chose to reject rather than reopen the dialog; or, for showDirectoryPicker(), the permission request did not end in "granted". |
Later operations on the returned handles raise the File System Standard’s errors instead: NotAllowedError when permission is not granted, NotFoundError when the file was deleted or moved, NoModificationAllowedError when another writable or sync access handle holds a lock.
Examples
Section titled “Examples”Each example feature-detects the exact method it is about to call; "showOpenFilePicker" in window is false in Firefox and Safari even though both expose FileSystemFileHandle for OPFS.
Opening, editing, and saving a text file, with an input and download fallback
Section titled “Opening, editing, and saving a text file, with an input and download fallback”Where the pickers exist, the user re-saves over the file they opened. Where they do not, <input type="file"> reads a file and an <a download> click writes a new copy; the fallback cannot overwrite the original, so the UI should say “Download” rather than “Save”.
let fileHandle = null;const editor = document.querySelector("#editor");const legacyInput = document.querySelector("#legacy-file");const supported = "showOpenFilePicker" in window;
document.querySelector("#open").addEventListener("click", async () => { if (!supported) { legacyInput.click(); return; } try { [fileHandle] = await window.showOpenFilePicker({ types: [{ description: "Text", accept: { "text/plain": [".txt", ".md"] } }], }); editor.value = await (await fileHandle.getFile()).text(); } catch (err) { if (err.name !== "AbortError") throw err; // dismissing the dialog is normal }});
legacyInput.addEventListener("change", async () => { editor.value = await legacyInput.files[0].text();});
document.querySelector("#save").addEventListener("click", async () => { if (!supported) { const link = document.createElement("a"); link.href = URL.createObjectURL(new Blob([editor.value], { type: "text/plain" })); link.download = "document.txt"; link.click(); URL.revokeObjectURL(link.href); return; } fileHandle ??= await window.showSaveFilePicker({ suggestedName: "document.txt" }); const writable = await fileHandle.createWritable(); await writable.write(editor.value); await writable.close(); // the file on disk changes only here});Writes go to a temporary file and replace the target when close() resolves; a tab closed before close() leaves the original untouched.
Restoring a handle from IndexedDB and re-checking permission
Section titled “Restoring a handle from IndexedDB and re-checking permission”Handles are structured-cloneable, so a “recent files” list can store them. The permission may or may not survive; call queryPermission() first and requestPermission() only from a click when the state is "prompt".
async function reopen(db, button) { if (!("showOpenFilePicker" in window)) return null;
const handle = await db.get("recent", "last"); // a file handle stored by an earlier pick if (!handle) return null;
const mode = { mode: "readwrite" }; let state = await handle.queryPermission(mode); if (state === "prompt") { await new Promise((resolve) => button.addEventListener("click", resolve, { once: true })); state = await handle.requestPermission(mode); } return state === "granted" ? handle : null;}A null result sends the caller back to the picker; a "denied" state stays denied until the user changes it in site settings.
See also
Section titled “See also”- Manifest file_handlers, the other half of a file-editing app: registering the PWA as the opener for a file type
- Handle files
- Origin Private File System (OPFS), the cross-engine, prompt-free part of the same handle API
- Storage Buckets API
- File System Access: showOpenFilePicker() method (wicg.github.io)
- File System Standard: FileSystemFileHandle interface (fs.spec.whatwg.org)
- The File System Access API: simplifying access to local files (developer.chrome.com)
Specifications
| Specification | Status |
|---|---|
| File System Access API | WICG draft |
| File System Access: showOpenFilePicker() method | WICG draft |
| File System Access: showSaveFilePicker() method | WICG draft |
| File System Access: showDirectoryPicker() method | WICG draft |
| File System Standard: FileSystemFileHandle interface | WHATWG living standard |
- Legend
- Yes
- Partial
- Flag
- No
- Unknown
| Browser / Platform | Support | Versions | Confidence | Source | Notes |
|---|---|---|---|---|---|
| Chrome (Android) | No | — | medium | source | 1 |
| Chrome (Desktop) | Yes | 86 | medium | source | — |
| Edge (Desktop) | Yes | 86 | medium | source | — |
| Safari (iOS) | No | — | medium | source | 2 |
| Safari (macOS) | No | — | medium | source | 3 |
| Firefox (Desktop) | No | — | medium | source | 4 |
| Samsung Internet | No | — | medium | source | 5 |
- showOpenFilePicker/showSaveFilePicker are not exposed on Android.
- Only the origin-private file system (OPFS) is available, not the user-visible picker.
- No showOpenFilePicker; OPFS only.
- OPFS only; no user-visible file picker access.
- The picker methods (`showOpenFilePicker`, `showSaveFilePicker`, `showDirectoryPicker`) are desktop-only in Chromium; Chrome for Android has no implementation for Samsung Internet to mirror (MDN compatibility table, checked 2026-10-03).