Folder Dialogs
Learn how to implement native folder picker dialogs in a Tauri v2 app using the dialog plugin including single and multiple directory selection from React and Rust
Understanding Folder Dialogs
A folder dialog is the native operating system window that lets a user browse their file system and select a directory. Instead of choosing a single file, the user picks an entire folder — the dialog returns the path to that folder so your application can work with everything inside it.
In a Tauri application, folder dialogs are built on top of the file dialog system. The same open function that selects files also selects directories when you set the directory flag to true. This means the permissions, capabilities, and usage patterns are nearly identical to file dialogs, with a few folder‑specific considerations.
You might need a folder dialog when your app requires the user to point to a workspace, an output location, a project directory, or a folder full of assets to process. Rather than typing a path manually — which is error‑prone — the native dialog gives the user a familiar, safe way to choose a folder.
Folder dialogs vs file dialogs:
Under the hood, Tauri uses the same system API for both. The difference is a single boolean flag that tells the operating system to show a folder picker instead of a file picker.
Prerequisites
Folder dialogs require the Tauri dialog plugin. If you have already set up the plugin while working with file dialogs, you can skip to the permissions section. Otherwise, make sure the plugin is added to your project.
The setup process involves both the Rust side (the backend plugin) and the JavaScript side (the npm package). Follow the steps that match your current project state.
Step 1: Add the dialog plugin to your Tauri project
Run the automatic installer from your terminal. This command updates both the Rust and JavaScript dependencies.
npm run tauri add dialog
If you prefer to install manually, add the Rust crate and the npm package separately:
cargo add tauri-plugin-dialog
npm install @tauri-apps/plugin-dialog
Step 2: Register the plugin in the Rust backend
Open src-tauri/src/lib.rs and add the dialog plugin to the builder. If this file already initializes other plugins, append the new one.
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
.plugin(tauri_plugin_dialog::init())
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
Permissions and Capabilities
Even with the plugin installed, Tauri’s security model blocks all native API access by default. To open a folder dialog, your app needs explicit permission to use the open command.
The dialog plugin ships with a default permission set that grants access to all dialog types (allow-message, allow-save, allow-open). If you are using the default capability generated by create-tauri-app, folder dialogs should already work. If you have a custom capability file, ensure it includes the dialog:allow-open permission.
Here is a minimal capability file that enables folder dialogs. It allows the frontend to invoke the open dialog from any window.
{
"identifier": "default",
"description": "Capability for the main window",
"windows": ["main"],
"permissions": [
"core:default",
"dialog:allow-open"
]
}
Missing permission blocks the dialog entirely:
If you call the open function without the dialog:allow-open permission, Tauri will reject the request. In the browser console you will see an error like Permission denied. The dialog will never appear.
Permission check passed:
If the native folder picker opens when you call open(), your capability configuration is correct and the plugin is communicating with the frontend.
Opening a Folder Dialog
The API for folder dialogs is exposed both in JavaScript (for your React frontend) and in Rust (for backend commands). Pick the environment that fits your use case.
Import the open function from @tauri-apps/plugin-dialog and call it with the directory option set to true. The function returns a string (the folder path) or null if the user cancels.
import { useState } from "react";
import { open } from "@tauri-apps/plugin-dialog";
function App() {
const [folderPath, setFolderPath] = useState<string | null>(null);
async function pickFolder() {
const selected = await open({
directory: true,
multiple: false,
});
setFolderPath(selected);
}
return (
<div>
<button onClick={pickFolder}>Select folder</button>
{folderPath && <p>Chosen folder: {folderPath}</p>}
</div>
);
}
export default App;
The open call is asynchronous. The dialog blocks the frontend event loop while it is visible, but the JavaScript thread remains responsive because the dialog itself is handled by the operating system. Once the user chooses a folder or dismisses the dialog, the promise resolves.
Blocking the main thread in Rust:
The blocking_pick_file method stops the Rust thread that calls it until the dialog closes. If you call it from the main Tauri thread (for example, inside a command invoked by the frontend), the rest of your Rust code that runs on the same thread will wait. This is usually fine for quick folder selections, but avoid doing heavy computation while the dialog is open. If you need non‑blocking behaviour, use the .pick_file(callback) variant instead.
Selecting a Single Folder
The example above already demonstrates single‑folder selection. When multiple is set to false (the default), the dialog returns a single path or null.
A typical pattern in React is to store the path in state and immediately use it — for instance, to display its contents or pass it to another function. The returned path is an absolute filesystem path, such as /home/user/Documents on Linux or C:\Users\User\Documents on Windows.
From Rust, the single folder path comes back as Option<FilePath>. You can then pass it to std::fs functions to read directory entries:
if let Some(path) = folder {
let entries = std::fs::read_dir(path.to_string()).unwrap();
for entry in entries {
println!("{:?}", entry.unwrap().path());
}
}
This makes it straightforward to build backend logic that reacts to the user’s choice.
Selecting Multiple Folders
You can allow the user to pick several folders at once by enabling the multiple flag.
In JavaScript, set multiple: true. The return type changes: you get an array of strings (string[]) or null.
async function pickFolders() {
const folders = await open({
directory: true,
multiple: true,
});
if (folders) {
console.log("Selected folders:", folders);
// folders is string[]
}
}
On the Rust side, use .set_multiple(true) and blocking_pick_files (note the plural). This returns Option<Vec<FilePath>>.
let folders = app
.dialog()
.file()
.set_directory(true)
.set_multiple(true)
.blocking_pick_files();
if let Some(paths) = folders {
for p in paths {
println!("{}", p.to_string());
}
}
Multiple folder selection on different platforms:
Windows and macOS support native multi‑folder selection natively. On Linux, the behaviour depends on the desktop environment; some file managers treat the dialog as a single‑select folder picker even when multiple is requested. Test on your target Linux distribution to confirm the experience.
Handling User Cancellation
A user who closes the dialog without choosing a folder triggers a null (JavaScript) or None (Rust) return value. Your code must handle this case gracefully — don’t assume a path will always exist.
In a React component, check for null before updating the UI:
const selected = await open({ directory: true });
if (selected !== null) {
setFolderPath(selected);
} else {
console.log("User cancelled the folder dialog");
}
From Rust, pattern‑match the Option:
match app.dialog().file().set_directory(true).blocking_pick_file() {
Some(path) => {
// use the path
}
None => {
eprintln!("No folder selected");
}
}
Skipping this check leads to runtime errors the moment a user decides not to pick a folder.
Platform Limitations
Not every platform supports folder dialogs equally.
- Windows and macOS have full support.
- Linux support is generally solid but the dialog appearance and multi‑select behaviour vary across desktop environments.
- Android and iOS do not support folder picker dialogs at all.
Folder dialogs not available on mobile:
Calling open({ directory: true }) on Android or iOS will result in an error or an immediate rejection. Always guard mobile code paths that use folder dialogs, and provide an alternative input method (for example, a manual path text field) if your app targets mobile platforms.
You can check the platform at runtime using Tauri’s os plugin to conditionally show the folder picker button.
Practical Use Cases
Folder dialogs appear in many real‑world Tauri applications. A few concrete patterns:
- Workspace selector: Let the user choose the root folder of a project they want to open. The app then reads all files inside and populates an editor or a file tree.
- Export destination: Before saving a batch of generated files (images, reports, configs), ask for a target folder. Combine the chosen path with file names to create the final output paths.
- Import assets: When a user wants to add a library of assets (textures, audio files, documents), use a folder dialog to let them point to the entire directory, then recursively read and import each file.
- Backup location: A user picks a folder where the application should store backups or logs. Store the selected path in a configuration file for future sessions.
In each case, the folder dialog removes the guesswork from path entry and gives the user a visual, trusted way to choose a directory on their own system.
Troubleshooting
| Symptom | Likely cause | Solution |
|---|---|---|
| Dialog does not open; console shows permission error | Missing dialog:allow-open in capability file | Add the permission to the appropriate capability file |
| Dialog opens for files instead of folders | directory option not set to true | Set directory: true in the options object (JS) or .set_directory(true) (Rust) |
App freezes after calling blocking_pick_file from the main thread | The thread is blocked waiting for the dialog, which may interfere with other operations | Switch to the non‑blocking .pick_file() callback, or move the call to a separate thread |
null even though a folder was selected | Dialog was cancelled, or the result is being read before the promise resolves | Always await the promise and check for null |
| Multiple folder selection returns only one folder on Linux | Desktop environment limitation | Fall back to allowing only single‑folder selection on Linux, or warn users about the limitation |
Repeated dialog openings can cause instability:
Rapidly opening and closing multiple dialogs (for example, by clicking the button several times in quick succession) can occasionally cause the native dialog system to become unresponsive on some platforms. Throttle or disable the trigger while a dialog is already open.
Summary
Folder dialogs in Tauri v2 are a thin wrapper around the file dialog system, activated by a single boolean flag. They bring the native operating system folder picker into your web‑based frontend without any heavy abstraction.
The key points to remember:
- Use
open({ directory: true })from JavaScript or.set_directory(true)in Rust. - Handle cancellation by checking for
null/None. - Permissions are mandatory — without
dialog:allow-open, nothing works. - On mobile platforms, folder dialogs are not available.
- The
multipleflag lets users select several folders, with platform‑dependent behaviour on Linux.