Window.showSaveFilePicker()

Enable sophisticated file saving in web applications with the File System Access API's showSaveFilePicker method

Understanding the File System Access API

The File System Access API represents a significant advancement in web platform capabilities, enabling powerful interactions with files on users' local devices. This API enables developers to build applications that rival native software in file manipulation capabilities, spanning use cases from web development services for code editors and design tools to document processors and data analysis platforms.

Unlike the traditional <input type="file"> approach or the download attribute method, the File System Access API provides bidirectional file operations. Web applications can not only read files selected by users but also save modifications directly back to the original files or create new files at user-specified locations.

The showSaveFilePicker() method of the Window interface displays a file picker dialog that allows users to specify where to save a file. Unlike simple download triggers, this method returns a FileSystemFileHandle that provides ongoing access to the selected file, enabling multiple write operations without requiring repeated user interaction.

Syntax and Parameters

Method Signature

showSaveFilePicker()
showSaveFilePicker(options)

The method accepts an optional SaveFilePickerOptions object that configures the file picker's behavior and appearance.

Available Options

types - An array of file type filters that determines which file formats appear in the picker:

const options = {
 types: [
 {
 description: 'Text Documents',
 accept: {
 'text/plain': ['.txt', '.md']
 }
 },
 {
 description: 'Spreadsheets',
 accept: {
 'text/csv': ['.csv'],
 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': ['.xlsx']
 }
 }
 ]
};

suggestedName - Provides a default filename:

const options = {
 suggestedName: `report-${new Date().toISOString().split('T')[0]}.pdf`
};

startIn - Specifies the initial directory:

const options = {
 startIn: 'documents' // desktop, documents, downloads, music, pictures, or videos
};

id - Helps the browser remember the user's choice:

const options = {
 id: 'project-exports'
};

excludeAcceptAllOption - When true, removes "All Files" option:

const options = {
 excludeAcceptAllOption: true
};

Practical Implementation

Basic Save Operation

async function saveTextFile(content) {
 const options = {
 types: [{
 description: 'Text file',
 accept: { 'text/plain': ['.txt'] }
 }],
 suggestedName: 'untitled.txt'
 };

 try {
 const handle = await window.showSaveFilePicker(options);
 const writable = await handle.createWritable();
 await writable.write(content);
 await writable.close();
 console.log('File saved successfully');
 } catch (error) {
 if (error.name === 'AbortError') {
 console.log('User cancelled the save operation');
 } else {
 console.error('Error saving file:', error);
 }
 }
}

Saving Different Content Types

JSON Data:

async function saveJsonFile(data, filename = 'data.json') {
 const handle = await window.showSaveFilePicker({
 suggestedName: filename,
 types: [{
 description: 'JSON Files',
 accept: { 'application/json': ['.json'] }
 }]
 });

 const writable = await handle.createWritable();
 await writable.write(JSON.stringify(data, null, 2));
 await writable.close();
}

Binary Data (Images):

async function saveImage(blob) {
 const handle = await window.showSaveFilePicker({
 suggestedName: 'image.png',
 types: [{
 description: 'PNG Images',
 accept: { 'image/png': ['.png'] }
 }]
 });

 const writable = await handle.createWritable();
 await writable.write(blob);
 await writable.close();
}

Error Handling

Common Exceptions

AbortError - User dismissed the file picker:

try {
 const handle = await window.showSaveFilePicker(options);
} catch (error) {
 if (error.name === 'AbortError') {
 // User cancelled - this is expected behavior
 return;
 }
 throw error;
}

SecurityError - Called without secure context or user activation:

async function secureSave(content) {
 if (!window.showSaveFilePicker) {
 throw new Error('File System Access API not supported');
 }
 
 try {
 const handle = await window.showSaveFilePicker(options);
 } catch (error) {
 if (error.name === 'SecurityError') {
 console.error('Security context required: ensure page is served over HTTPS');
 }
 }
}

TypeError - Invalid file type configurations:

function validateOptions(options) {
 if (options.types && options.types.length === 0) {
 throw new TypeError('At least one file type must be specified');
 }
 
 options.types?.forEach(type => {
 if (!type.accept || Object.keys(type.accept).length === 0) {
 throw new TypeError('Each type must have at least one MIME type mapping');
 }
 });
}
Browser Compatibility
BrowserVersionStatus
Chrome86+Full Support
Edge86+Full Support
FirefoxNot SupportedBehind flag
SafariNot SupportedNo public signals
Opera72+Full Support

Progressive Enhancement Pattern

async function saveFileWithFallback(content, filename, mimeType = 'text/plain') {
 // Try modern API first
 if ('showSaveFilePicker' in window) {
 try {
 const handle = await window.showSaveFilePicker({
 suggestedName: filename,
 types: [{
 description: 'File',
 accept: { [mimeType]: [filename.split('.').pop()] }
 }]
 });

 const writable = await handle.createWritable();
 await writable.write(content);
 await writable.close();
 return true;
 } catch (error) {
 if (error.name !== 'AbortError') {
 console.warn('Modern API failed, falling back:', error);
 }
 }
 }

 // Fallback to traditional download
 const blob = new Blob([content], { type: mimeType });
 const url = URL.createObjectURL(blob);
 const a = document.createElement('a');
 a.href = url;
 a.download = filename;
 document.body.appendChild(a);
 a.click();
 document.body.removeChild(a);
 URL.revokeObjectURL(url);
 
 return true;
}

Feature Detection

function isFileSystemAccessSupported() {
 return 'showSaveFilePicker' in window && 
 'createWritable' in FileSystemFileHandle.prototype;
}
Best Practices

Include File Type Filters

Help users understand what your application supports by providing clear descriptions and MIME type mappings.

Provide Sensible Defaults

Use suggestedName to offer meaningful default filenames based on the content being saved.

Handle Cancellation Gracefully

Treat AbortError as an expected user action, not an error condition.

Implement Progressive Enhancement

Provide fallback download functionality for browsers without File System Access API support.

Clean Up Properly

Always close writable streams and remove temporary handles when they're no longer needed.

Consider Large File Handling

Use streaming for files larger than available memory.

Frequently Asked Questions

Conclusion

The showSaveFilePicker() method represents a significant step forward in web application capabilities, enabling sophisticated file handling that previously required native software. By providing a standardized, secure, and user-controlled approach to file operations, the File System Access API empowers developers to build powerful tools that rival traditional desktop applications. When implementing advanced file handling features, partnering with experienced web development professionals can ensure robust, secure, and user-friendly implementations.

As browser support continues to expand and the specification matures, we can expect to see increasingly sophisticated file-based applications emerge on the web platform. The integration of modern web APIs like the File System Access API is particularly valuable for AI automation solutions that need to process and export data files. Developers who adopt these patterns now will be well-positioned to deliver the rich, capable experiences that modern users expect.


Sources:

Ready to Build Advanced File Handling?

Our team of web development experts can help you implement sophisticated file operations using modern web APIs.