One of the most common tasks when building desktop applications is interacting with the user's file system. This often involves allowing users to open existing files or save new ones. While you could build custom UI elements for this, Electron provides convenient access to the operating system's native file dialogs. This means your app will present a familiar and intuitive file browser that users on Windows, macOS, and Linux are already accustomed to. This not only enhances the user experience but also saves you significant development time.
Electron's dialog module is your gateway to these native dialogs. It's part of the electron module, so you can import it directly into your renderer process or main process code. For security and performance reasons, the dialog module is primarily intended for use in the main process, but we'll explore how to trigger it from the renderer process.
const { dialog } = require('electron');The most common dialogs you'll use are for opening and saving files. The dialog.showOpenDialog() method allows users to select one or more files, while dialog.showSaveDialog() prompts the user to specify a location and filename for saving.
Let's start with opening a file. The showOpenDialog() method returns a Promise that resolves with an object containing filePaths (an array of strings representing the selected file paths) and canceled (a boolean indicating if the dialog was canceled). Here's an example of how you might use it:
async function openFile() {
const { canceled, filePaths } = await dialog.showOpenDialog({
title: 'Open a File',
buttonLabel: 'Select',
properties: ['openFile', 'multiSelections']
});
if (canceled) {
console.log('User canceled file selection.');
return;
}
console.log('Selected files:', filePaths);
// Here you would typically read the content of the selected files
}In the showOpenDialog() configuration, we can specify several options:
title: The title of the dialog window.buttonLabel: The text displayed on the confirmation button (e.g., 'Open', 'Select').properties: An array that dictates the behavior of the dialog. Some common values include:'openFile': Allows selection of files.'openDirectory': Allows selection of directories.'multiSelections': Allows selection of multiple files or directories.'createDirectory': Allows the user to create a new directory within the dialog.'showHiddenFiles': Shows hidden files in the dialog.
Now, let's look at saving a file. The dialog.showSaveDialog() method works similarly, returning a Promise that resolves with an object containing filePath (the chosen path and filename) and canceled (a boolean indicating cancellation).
async function saveFile() {
const { canceled, filePath } = await dialog.showSaveDialog({
title: 'Save File As',
defaultPath: 'my-document.txt',
filters: [
{ name: 'Text Files', extensions: ['txt'] },
{ name: 'All Files', extensions: ['*'] }
]
});
if (canceled) {
console.log('User canceled file save.');
return;
}
console.log('File will be saved to:', filePath);
// Here you would write the content to the specified filePath
}For showSaveDialog(), notable options include:
defaultPath: The initial filename or path that appears in the dialog.filters: An array of objects that define file type filters. Each object has aname(user-friendly name) andextensions(an array of file extensions).
It's important to remember that these dialogs are typically triggered by user actions in the renderer process (e.g., clicking a button). To call dialog methods from the renderer process, you'll need to use IPC (Inter-Process Communication) to send a message to the main process, which will then invoke the dialog and send the result back to the renderer.
graph TD
A[Renderer Process] --> B{User Clicks Button};
B --> C[Send IPC Message 'open-file'];
C --> D[Main Process];
D --> E[dialog.showOpenDialog()];
E --> F{Dialog Shown to User};
F --> G{User Selects File(s) or Cancels};
G -- File(s) Selected --> H[Return File Paths];
G -- Canceled --> I[Return Canceled State];
H --> D;
I --> D;
D --> J[Send IPC Reply to Renderer];
J --> A;
A --> K[Handle File Paths or Cancellation];
To implement this IPC communication, you would set up listeners in both your main and renderer processes. In the main process, you'd listen for the 'open-file' or 'save-file' channel, execute the dialog, and send the result back. In the renderer process, you'd send the IPC message and then handle the reply.