Browser FS Access API Ponyfill
browser-fs-access is a JavaScript library that provides a simplified, progressive enhancement approach to using the File System Access API in web browsers. It acts as a ponyfill, meaning it offers a consistent API surface while transparently falling back to older, less capable methods like `<input type="file">` and `<a download>` on browsers that do not fully support the File System Access API. This ensures broad compatibility while leveraging modern capabilities where available. The library is currently at version 0.38.0 and maintains an active release cadence, frequently publishing updates and fixes, often on a monthly basis. Key differentiators include its robust fallback mechanism, its origin from GoogleChromeLabs, and its focus on developer ergonomics by abstracting away the complexities of feature detection and legacy API interactions. It ships with TypeScript types for improved developer experience.
Common errors
-
SyntaxError: Unexpected token 'export' (when using with Jest or older bundlers)
cause The library is distributed as an ESM module, and older versions of Jest or some bundler configurations might not correctly transpile `export` syntax in `node_modules`.fixConfigure Jest's `transformIgnorePatterns` to include `browser-fs-access` for transformation, e.g., `"transformIgnorePatterns": ["node_modules/(?!browser-fs-access)"]`. For other bundlers, ensure your configuration correctly handles ESM in `node_modules`.
Warnings
- breaking The `directoryOpen()` function's return value for empty directories changed. When the File System Access API is supported and a directory is empty, the function now returns `[directoryHandle]` instead of an empty array `[]`. This is to provide access to the directory handle itself.
- gotcha `browser-fs-access` is a ponyfill, not a polyfill. This means its behavior, user experience (UI for file pickers), and capabilities will differ significantly between browsers fully supporting the File System Access API and those relying on legacy fallbacks. Always test your application across all target browsers to understand these UX variations.
- gotcha Prior to versions 0.33.0 and 0.35.0, there were known issues with proper detection and use of the File System Access API within same-origin and cross-origin iframes, sometimes leading to erroneous fallback usage. While fixed, complex embedding scenarios might still require careful testing.
- gotcha When using `fileSave` to overwrite an existing file, the File System Access API will prompt the user for permission. In legacy fallback mode, this functionality is not available, and typically triggers a new download instead of overwriting, leading to a different user experience.
Install
-
npm install browser-fs-access -
yarn add browser-fs-access -
pnpm add browser-fs-access
Imports
- fileOpen
const { fileOpen } = require('browser-fs-access');import { fileOpen } from 'browser-fs-access'; - directoryOpen
const directoryOpen = require('browser-fs-access').directoryOpen;import { directoryOpen } from 'browser-fs-access'; - fileSave
import fileSave from 'browser-fs-access'; // Incorrect default import
import { fileSave } from 'browser-fs-access'; - supported
import * as fsAccess from 'browser-fs-access'; console.log(fsAccess.supported);
import { supported } from 'browser-fs-access'; - FileSystemAccessTypes
import type { FileSystemAccessTypes } from 'browser-fs-access';
Quickstart
import { fileOpen, directoryOpen, fileSave, supported } from 'browser-fs-access';
async function performFileOperations() {
if (supported) {
console.log('Using the File System Access API.');
} else {
console.log('Using the fallback implementation (e.g., <input type="file"> or <a download>).');
}
try {
// Open a single image file with specific options
const imageBlob = await fileOpen({
mimeTypes: ['image/*'],
extensions: ['.png', '.jpg', '.jpeg'],
description: 'Select an image file',
multiple: false,
startIn: 'pictures'
});
console.log(`Opened image: ${imageBlob.name} (type: ${imageBlob.type}, size: ${imageBlob.size} bytes)`);
// Open multiple text files from a directory, recursively
const filesInDir = await directoryOpen({
recursive: true,
mode: 'read',
mimeTypes: ['text/*', 'application/json'],
extensions: ['.txt', '.md', '.json']
});
console.log(`Opened ${filesInDir.length} files in directory. First file: ${filesInDir[0]?.name || 'N/A'}`);
// Save a new text file created from a Blob
const textContent = 'This is a test file saved using browser-fs-access.\nHello, world!';
const textBlob = new Blob([textContent], { type: 'text/plain' });
await fileSave(textBlob, {
fileName: 'my-document.txt',
extensions: ['.txt'],
description: 'My Text Document',
startIn: 'documents'
});
console.log('Successfully saved my-document.txt');
} catch (error) {
if (error.name === 'AbortError') {
console.log('User cancelled the file picker.');
} else {
console.error('File operation failed:', error);
}
}
}
performFileOperations();