showOpenFilePicker()

Opens the system's file picker dialog for selecting existing files to read. Returns an array of FileSystemFileHandle objects.

showSaveFilePicker()

Opens the system's save dialog for creating new files or overwriting existing ones. Returns a single FileSystemFileHandle.

showDirectoryPicker()

Opens the system directory picker for selecting or creating folder access. Returns a FileSystemDirectoryHandle.

1// Core picker methods2 3// Open file(s) for reading4const [handle] = await window.showOpenFilePicker({5  types: [6    {7      description: 'Text files',8      accept: { 'text/plain': ['.txt', '.md', '.json'] }9    }10  ],11  multiple: false12});13 14// Save file to disk15const saveHandle = await window.showSaveFilePicker({16  suggestedName: 'document.txt',17  types: [18    {19      description: 'Text document',20      accept: { 'text/plain': ['.txt'] }21    }22  ]23});24 25// Select directory for batch operations26const dirHandle = await window.showDirectoryPicker();27 28// Read file contents from handle29const file = await handle.getFile();30const contents = await file.text();31 32// Write to file via writable stream33const writable = await saveHandle.createWritable();34await writable.write('Hello, File System!');35await writable.close();
1// Reading files with getFile()2 3async function readTextFile(handle) {4  const file = await handle.getFile();5  return await file.text();6}7 8async function readJsonFile(handle) {9  const file = await handle.getFile();10  const text = await file.text();11  return JSON.parse(text);12}13 14async function readLargeFile(handle) {15  const file = await handle.getFile();16  const stream = file.stream();17  const reader = stream.getReader();18 19  while (true) {20    const { done, value } = await reader.read();21    if (done) break;22    // Process chunk (value is Uint8Array)23    console.log('Received chunk:', value.length, 'bytes');24  }25}26 27// Handle errors gracefully28async function safeReadFile(handle) {29  try {30    const file = await handle.getFile();31    return await file.text();32  } catch (error) {33    if (error.name === 'NotFoundError') {34      throw new Error('File has been deleted or moved');35    }36    throw error;37  }38}
1// Writing files with createWritable()2 3async function writeTextFile(handle, content) {4  const writable = await handle.createWritable();5  await writable.write(content);6  await writable.close();7}8 9async function writeJsonFile(handle, data) {10  const writable = await handle.createWritable();11  await writable.write(JSON.stringify(data, null, 2));12  await writable.close();13}14 15async function appendToFile(handle, content) {16  const writable = await handle.createWritable({ keepExistingData: true });17  await writable.seek(0); // Move to end of existing data18  await writable.write(content);19  await writable.close();20}21 22// Write from a blob or buffer23async function writeBuffer(handle, buffer) {24  const writable = await handle.createWritable();25  await writable.write(buffer);26  await writable.close();27}28 29// Using write options for truncate or append30async function writeWithOptions(handle, content, mode = 'truncate') {31  const writable = await handle.createWritable({32    keepExistingData: mode === 'append'33  });34 35  if (mode === 'truncate') {36    await writable.write({ size: 0 }); // Clear file37  }38 39  await writable.write(content);40  await writable.close();41}
1// Directory operations2 3// List all items in a directory4async function listDirectory(dirHandle) {5  const items = [];6  for await (const [name, handle] of dirHandle.entries()) {7    items.push({8      name,9      kind: handle.kind, // 'file' or 'directory'10      handle11    });12  }13  return items;14}15 16// Get or create nested handles17async function getOrCreateFile(dirHandle, filename) {18  return dirHandle.getFileHandle(filename, { create: true });19}20 21async function getOrCreateDir(dirHandle, dirname) {22  return dirHandle.getDirectoryHandle(dirname, { create: true });23}24 25// Remove a file or directory26async function removeItem(dirHandle, name) {27  await dirHandle.removeEntry(name, { recursive: true });28}29 30// Recursively walk directory tree31async function* walkDirectory(dirHandle, path = '') {32  for await (const [name, handle] of dirHandle.entries()) {33    const fullPath = path ? `${path}/${name}` : name;34    if (handle.kind === 'directory') {35      yield { path: fullPath, kind: 'directory', handle };36      yield* walkDirectory(handle, fullPath);37    } else {38      yield { path: fullPath, kind: 'file', handle };39    }40  }41}42 43// Count files in directory tree44async function countFiles(dirHandle) {45  let count = 0;46  for await (const item of walkDirectory(dirHandle)) {47    if (item.kind === 'file') count++;48  }49  return count;50}
1// Permission management2 3async function requestFilePermission(handle, readOnly = true) {4  const options = { mode: readOnly ? 'read' : 'readwrite' };5 6  // Check current permission state7  const status = await handle.queryPermission(options);8 9  if (status === 'granted') {10    return true;11  }12 13  if (status === 'denied') {14    return false;15  }16 17  // Request permission from user18  try {19    const result = await handle.requestPermission(options);20    return result === 'granted';21  } catch (error) {22    console.error('Permission request failed:', error);23    return false;24  }25}26 27// Wrapper for file operations with auto-permission28async function withFilePermission(handle, operation, readOnly = true) {29  const hasPermission = await requestFilePermission(handle, readOnly);30  if (!hasPermission) {31    throw new Error('File permission denied');32  }33  return operation(handle);34}35 36// Check permission without prompting37async function hasAccess(handle, mode = 'read') {38  try {39    const status = await handle.queryPermission({ mode });40    return status === 'granted';41  } catch {42    return false;43  }44}45 46// Persist handle and request persistent access47async function openWithPersistence() {48  const [handle] = await window.showOpenFilePicker();49 50  // Request persistent permission51  const opts = { mode: 'readwrite', keepPermission: true };52  if ((await handle.queryPermission(opts)) === 'prompt') {53    await handle.requestPermission(opts);54  }55 56  return handle;57}
1// Browser compatibility helpers2 3// Check if File System Access API is supported4function isFileSystemAccessSupported() {5  return 'showOpenFilePicker' in window;6}7 8// Feature detection with graceful fallback9async function openFileWithFallback() {10  if (isFileSystemAccessSupported()) {11    const [handle] = await window.showOpenFilePicker();12    const file = await handle.getFile();13    return { handle, file, method: 'fs-access' };14  }15 16  // Fallback to traditional file input17  return new Promise((resolve) => {18    const input = document.createElement('input');19    input.type = 'file';20    input.accept = '.txt,.md,.json';21 22    input.onchange = (event) => {23      const file = event.target.files[0];24      resolve({ file, method: 'file-input' });25    };26 27    input.click();28  });29}30 31// Detect specific capabilities32async function getBrowserCapabilities() {33  const caps = {34    picker: 'showOpenFilePicker' in window,35    directory: 'showDirectoryPicker' in window,36    writable: false,37    syncHandle: false38  };39 40  if (caps.picker) {41    try {42      const testHandle = await window.showSaveFilePicker();43      caps.writable = true;44      testHandle.handle.cancel();45    } catch (e) {46      // Feature exists but failed to use47    }48  }49 50  return caps;51}52 53// UI message based on support54function getUnsupportedMessage() {55  const ua = navigator.userAgent;56 57  if (ua.includes('Firefox')) {58    return 'Firefox: Enable dom.webshell.enabled and dom.fileHandle.enabled in about:config';59  }60  if (ua.includes('Safari') && !ua.includes('Chrome')) {61    return 'Safari: Enable Experimental Features > File System Access API in Develop menu';62  }63  return 'Your browser does not support the File System Access API. Try Chrome, Edge, or Opera.';64}
1// Complete Document Editor using File System Access API2 3class DocumentEditor {4  constructor() {5    this.handle = null;6    this.filename = 'Untitled';7    this.content = '';8    this.unsavedChanges = false;9  }10 11  // Check for API support12  static isSupported() {13    return 'showOpenFilePicker' in window;14  }15 16  // Open existing file17  async open() {18    try {19      const [handle] = await window.showOpenFilePicker({20        types: [21          {22            description: 'Text Documents',23            accept: {24              'text/plain': ['.txt', '.md', '.json', '.html', '.css', '.js']25            }26          }27        ]28      });29 30      return await this.loadFromHandle(handle);31    } catch (error) {32      if (error.name !== 'AbortError') {33        console.error('Failed to open file:', error);34      }35      return false;36    }37  }38 39  // Load content from file handle40  async loadFromHandle(handle) {41    try {42      // Request read permission43      if ((await handle.queryPermission({ mode: 'read' })) === 'prompt') {44        await handle.requestPermission({ mode: 'read' });45      }46 47      const file = await handle.getFile();48      this.content = await file.text();49      this.filename = file.name;50      this.handle = handle;51      this.unsavedChanges = false;52 53      return true;54    } catch (error) {55      console.error('Failed to load file:', error);56      return false;57    }58  }59 60  // Create new file (shows save dialog)61  async create() {62    try {63      const handle = await window.showSaveFilePicker({64        suggestedName: `${this.filename}.txt`,65        types: [66          {67            description: 'Text Document',68            accept: { 'text/plain': ['.txt'] }69          },70          {71            description: 'Markdown',72            accept: { 'text/markdown': ['.md'] }73          }74        ]75      });76 77      this.content = '';78      this.filename = handle.name;79      this.handle = handle;80      this.unsavedChanges = false;81 82      return true;83    } catch (error) {84      if (error.name !== 'AbortError') {85        console.error('Failed to create file:', error);86      }87      return false;88    }89  }90 91  // Save current file92  async save() {93    if (!this.handle) {94      return await this.saveAs();95    }96 97    try {98      // Request write permission if needed99      if ((await this.handle.queryPermission({ mode: 'readwrite' })) === 'prompt') {100        await this.handle.requestPermission({ mode: 'readwrite' });101      }102 103      const writable = await this.handle.createWritable();104      await writable.write(this.content);105      await writable.close();106 107      this.unsavedChanges = false;108      return true;109    } catch (error) {110      console.error('Failed to save:', error);111      return false;112    }113  }114 115  // Save as new file116  async saveAs() {117    try {118      const handle = await window.showSaveFilePicker({119        suggestedName: this.filename,120        types: [121          {122            description: 'Text Document',123            accept: { 'text/plain': ['.txt'] }124          }125        ]126      });127 128      this.handle = handle;129      this.filename = handle.name;130 131      return await this.save();132    } catch (error) {133      if (error.name !== 'AbortError') {134        console.error('Failed to save as:', error);135      }136      return false;137    }138  }139 140  // Update content141  updateContent(newContent) {142    this.content = newContent;143    this.unsavedChanges = true;144  }145 146  // Get current content147  getContent() {148    return this.content;149  }150 151  // Check for unsaved changes152  hasUnsavedChanges() {153    return this.unsavedChanges;154  }155 156  // Get filename157  getFilename() {158    return this.filename;159  }160}161 162// Usage example163const editor = new DocumentEditor();164 165// Hook up to UI166document.getElementById('open-btn').onclick = () => editor.open();167document.getElementById('save-btn').onclick = () => editor.save();168document.getElementById('new-btn').onclick = () => editor.create();169 170// Handle content changes171document.getElementById('editor').oninput = (e) => {172  editor.updateContent(e.target.value);173};
1// Origin Private File System (OPFS) for performance2 3class OPFSManager {4  constructor() {5    this.root = null;6  }7 8  // Initialize OPFS access9  async init() {10    this.root = await navigator.storage.getDirectory();11    return this;12  }13 14  // Get or create a file in OPFS15  async getFileHandle(name, options = {}) {16    if (!this.root) await this.init();17    return this.root.getFileHandle(name, options);18  }19 20  // Get or create a directory in OPFS21  async getDirectoryHandle(name, options = {}) {22    if (!this.root) await this.init();23    return this.root.getDirectoryHandle(name, options);24  }25 26  // Write data to OPFS file27  async writeToOPFS(filename, content) {28    const handle = await this.getFileHandle(filename, { create: true });29    const writable = await handle.createWritable();30    await writable.write(content);31    await writable.close();32  }33 34  // Read data from OPFS file35  async readFromOPFS(filename) {36    const handle = await this.getFileHandle(filename);37    const file = await handle.getFile();38    return await file.text();39  }40 41  // List all files in OPFS root42  async listFiles() {43    if (!this.root) await this.init();44    const files = [];45    for await (const [name, handle] of this.root.entries()) {46      files.push({ name, kind: handle.kind });47    }48    return files;49  }50 51  // Delete file from OPFS52  async deleteFromOPFS(filename) {53    if (!this.root) await this.init();54    await this.root.removeEntry(filename);55  }56}57 58// Performance optimization example59async function processLargeFileOptimized(userFileHandle) {60  const opfs = new OPFSManager();61  await opfs.init();62 63  // Read user file64  const userFile = await userFileHandle.getFile();65  const chunks = [];66 67  // Process in chunks using OPFS for intermediate storage68  const stream = userFile.stream();69  const reader = stream.getReader();70  let chunkIndex = 0;71 72  while (true) {73    const { done, value } = await reader.read();74    if (done) break;75 76    // Store chunk in OPFS77    await opfs.writeToOPFS(`chunk-${chunkIndex}.tmp`, value);78    chunks.push(`chunk-${chunkIndex}.tmp`);79    chunkIndex++;80  }81 82  // Process each chunk from OPFS83  const processedChunks = [];84  for (const chunkName of chunks) {85    const data = await opfs.readFromOPFS(chunkName);86    const processed = await transformData(data); // Your processing function87    processedChunks.push(processed);88 89    // Clean up temp file90    await opfs.deleteFromOPFS(chunkName);91  }92 93  // Combine results94  return processedChunks.join('');95}
1// Best practices for File System Access API2 3class RobustFileHandler {4  constructor() {5    this.currentHandle = null;6  }7 8  // 1. Always use try-catch with specific error handling9  async safeOpen() {10    try {11      const [handle] = await window.showOpenFilePicker();12      return handle;13    } catch (error) {14      switch (error.name) {15        case 'AbortError':16          console.log('User cancelled the file picker');17          return null;18        case 'SecurityError':19          throw new Error('File access blocked by security policy');20        default:21          console.error('Unknown error:', error);22          throw error;23      }24    }25  }26 27  // 2. Request permissions before critical operations28  async ensurePermission(handle, readOnly = true) {29    const options = { mode: readOnly ? 'read' : 'readwrite' };30 31    try {32      const status = await handle.queryPermission(options);33      if (status === 'granted') return true;34      if (status === 'denied') return false;35 36      const result = await handle.requestPermission(options);37      return result === 'granted';38    } catch (error) {39      console.error('Permission error:', error);40      return false;41    }42  }43 44  // 3. Check file availability before operations45  async isFileAvailable(handle) {46    try {47      const file = await handle.getFile();48      // If we can get the file, it's available49      return true;50    } catch (error) {51      if (error.name === 'NotFoundError') return false;52      throw error; // Other errors should propagate53    }54  }55 56  // 4. Handle concurrent modifications57  async saveWithConflictCheck(handle, newContent) {58    // Get current file modification time59    const file = await handle.getFile();60    const lastModified = file.lastModified;61 62    // Check if file has been modified since we opened it63    if (this.lastKnownModified && lastModified !== this.lastKnownModified) {64      const userWantsMerge = confirm(65        'The file has been modified elsewhere. Overwrite anyway?'66      );67      if (!userWantsMerge) return false;68    }69 70    // Proceed with save71    const writable = await handle.createWritable();72    await writable.write(newContent);73    await writable.close();74 75    // Update our tracking76    this.lastKnownModified = (await handle.getFile()).lastModified;77    return true;78  }79 80  // 5. Provide undo capability with OPFS81  async createSnapshot(content, operation) {82    const opfs = await navigator.storage.getDirectory();83    const snapshotName = `snapshot-${Date.now()}-${operation}.txt`;84 85    const handle = await opfs.getFileHandle(snapshotName, { create: true });86    const writable = await handle.createWritable();87    await writable.write(content);88    await writable.close();89 90    return snapshotName;91  }92}93 94// 6. Use AbortController for cancellable operations95class CancellableFileReader {96  constructor() {97    this.abortController = new AbortController();98  }99 100  async readLargeFile(handle) {101    try {102      const file = await handle.getFile();103      const stream = file.stream();104      const reader = stream.getReader();105 106      while (true) {107        const { done, value } = await reader.read({108          signal: this.abortController.signal109        });110        if (done) break;111        // Process value...112      }113    } catch (error) {114      if (error.name === 'AbortError') {115        console.log('Read operation was cancelled');116        return;117      }118      throw error;119    }120  }121 122  cancel() {123    this.abortController.abort();124  }125}

Ready To Build Powerful File-Handling Web Applications?

Our team of experienced developers can help you implement the File System Access API and other modern web APIs to create seamless user experiences.

Sources

  1. WICG File System Access Specification - Official specification from the Web Platform Incubator Community Group
  2. Chrome Developer Documentation - File System Access API - Google's official implementation guide
  3. MDN Web Docs - File System API - Mozilla's comprehensive documentation
  4. Can I Use File System Access API - Browser compatibility data