System Directories

Learn how Tauri v2 system base directories map to real file system locations and how to use them to read, write, and manage files in a cross-platform desktop app.

Every desktop application needs a place to store user data, configuration, logs, or temporary files. Hard-coding paths like C:\Users\Alice\AppData or /home/alice breaks the moment your app runs on a different machine. Tauri solves this with system directories — a set of named base directories that resolve to the correct, platform-appropriate location at runtime.

This page covers every system directory Tauri provides, how each one maps to Windows, macOS, and Linux, and how to use them safely with both the path API and the file system plugin.

The BaseDirectory Enum

All system directories are represented by the BaseDirectory enum from @tauri-apps/api/path. You use it in two ways: to obtain an absolute path string via a path function, or to tell a file system operation which root directory to work from.

The full enum includes directories for the user’s home, common personal folders, standard OS paths, and app‑specific storage areas that are namespaced to your application’s bundle identifier.

User home and common folders

Enum MemberPath FunctionPurpose
HomehomeDir()The current user’s home directory.
DesktopdesktopDir()The user’s desktop.
DocumentdocumentDir()The user’s documents folder.
DownloaddownloadDir()The user’s downloads folder.
PicturepictureDir()The user’s pictures folder.
VideovideoDir()The user’s videos folder.
AudioaudioDir()The user’s music / audio folder.
PublicpublicDir()The user’s public / shared directory.
FontfontDir()The system fonts directory.
TemplatetemplateDir()The user’s document templates directory.
ExecutableexecutableDir()The directory containing the application’s executable.
ResourceresourceDir()The application’s resource directory (bundled assets).

Resource directory is often read‑only:

On macOS and Linux the resource directory is part of the application bundle and is not writable at runtime. Use it only for reading bundled assets. On Windows it may be writable, but relying on that is not cross‑platform safe.

System standard directories

Enum MemberPath FunctionPurpose
TemptempDir()System temporary directory.
CachecacheDir()User‑wide cache directory.
ConfigconfigDir()User‑wide configuration directory.
DatadataDir()User‑wide application data directory.
LocalDatalocalDataDir()User‑wide local (non‑roaming) data directory.
RuntimeruntimeDir()Per‑user runtime files (socket, pid) directory.

App‑namespaced directories

These directories are scoped to your application using the identifier from tauri.conf.json. They are the recommended locations for storing app‑specific data, configuration, and cache.

Enum MemberPath FunctionResolved pattern (simplified)
AppDataappDataDir()${dataDir}/${bundleIdentifier}
AppConfigappConfigDir()${configDir}/${bundleIdentifier}
AppCacheappCacheDir()${cacheDir}/${bundleIdentifier}
AppLocalDataappLocalDataDir()${localDataDir}/${bundleIdentifier}
AppLogappLogDir()OS‑specific log directory (see next section)

Resolved Paths by Platform

The same base directory resolves to a completely different absolute path on each operating system. The tabs below show the exact mapping for a selection of the most commonly used directories.

Base DirectoryTypical Resolved Path
HomeC:\Users\Alice
DesktopC:\Users\Alice\Desktop
DocumentC:\Users\Alice\Documents
DownloadC:\Users\Alice\Downloads
AppDataC:\Users\Alice\AppData\Roaming\com.tauri.app
AppLocalDataC:\Users\Alice\AppData\Local\com.tauri.app
AppConfigC:\Users\Alice\AppData\Roaming\com.tauri.app
AppCacheC:\Users\Alice\AppData\Local\com.tauri.app
TempC:\Users\Alice\AppData\Local\Temp

Linux follows XDG base directories:

The Linux mappings above assume a standard XDG setup. If the user has overridden environment variables like $XDG_DATA_HOME, the resolved paths will reflect those custom locations.

Using System Directories with the Path API

The @tauri-apps/api/path module provides a dedicated function for every base directory. Each function returns a Promise<string> containing the absolute path.

import { homeDir, appDataDir, join } from '@tauri-apps/api/path';
async function buildPaths() {
  const home = await homeDir();
  const appData = await appDataDir();
  const configFile = await join(appData, 'settings.json');
  console.log(home);       // e.g. /home/alice
  console.log(configFile); // e.g. /home/alice/.local/share/com.tauri.app/settings.json
}

The returned paths are plain strings, so you can compose them with join() or any other path manipulation function. This approach is useful when you need the absolute path for display purposes or to pass it to a Rust command.

Path functions do not create directories:

Getting a path via the API does not create the directory on disk. If you intend to write a file there, you must ensure the directory exists first.

A common beginner misconception is that homeDir() will return ~/ on macOS/Linux or %USERPROFILE% on Windows. It always returns the fully expanded absolute path; shell‑style shortcuts never appear.

Using System Directories with the File System Plugin

When you perform a file operation with the @tauri-apps/plugin-fs plugin, you can specify a baseDir from BaseDirectory instead of working with absolute paths. The plugin automatically resolves the base directory before performing the operation.

import { readTextFile, BaseDirectory } from '@tauri-apps/plugin-fs';
async function readSettings() {
  const content = await readTextFile('config/settings.json', {
    baseDir: BaseDirectory.AppConfig,
  });
  return JSON.parse(content);
}

The file path 'config/settings.json' is relative to the resolved AppConfig directory. The plugin prevents path traversal — paths containing ../ or absolute paths are rejected.

Forbidden path errors mean missing scope:

If you see an error like forbidden path: config/settings.json, it does not mean the directory is inaccessible. It means your capability file does not grant the plugin permission to access that base directory. Always check your fs:scope permissions first.

The same pattern applies for writing, creating directories, or checking existence.

import { mkdir, writeTextFile, BaseDirectory } from '@tauri-apps/plugin-fs';
async function initializeAppData() {
  await mkdir('logs', { baseDir: BaseDirectory.AppData, recursive: true });
  await writeTextFile('logs/startup.txt', 'Application started', {
    baseDir: BaseDirectory.AppData,
  });
}

Recursive creation avoids intermediate directory errors:

When you pass { recursive: true } to mkdir, it creates all missing parent directories. Without it, you will get an os error 3 (“The system cannot find the path specified”) if any parent folder does not already exist.

Permissions and Capability Scoping

Tauri’s security model requires you to explicitly declare which directories your application may access. The file system plugin will refuse any operation that falls outside the declared scope, even if the base directory itself is valid.

The scope is configured in your capability file, typically src-tauri/capabilities/default.json:

src-tauri/capabilities/default.json
{
  "identifier": "default",
  "description": "Default capabilities for the main window",
  "windows": ["main"],
  "permissions": [
    "core:default",
    "fs:default",
    {
      "identifier": "fs:scope",
      "allow": [
        { "path": "$APPDATA/**" },
        { "path": "$APPCONFIG/**" },
        { "path": "$DOWNLOAD/**" }
      ]
    }
  ]
}

The variables $APPDATA, $APPCONFIG, and $DOWNLOAD are scope patterns that map to the same platform‑specific paths as the corresponding BaseDirectory members. The /** suffix permits any file or subdirectory within that tree.

Each directory must be listed individually:

There is no wildcard that grants “all directories”. You must add an entry for each base directory your app actually needs to access. A common mistake is adding only $APPDATA/** and then wondering why a BaseDirectory.Download operation fails.

If your application needs to read from the user’s entire home directory, you can use:

{ "path": "$HOME/**" }

Be cautious with broad permissions; only grant what the feature genuinely requires.

Choosing the Right Directory for Your Data

Not every directory is interchangeable. Picking the wrong one can result in lost user data, permission errors, or cluttered folders. Here is a decision framework for common scenarios:

  • User‑created content (documents, images, exports): Use Document, Desktop, or Download, depending on the natural user expectation. If your app exports reports, saving them to Document is generally correct.
  • Application settings and preferences: Use AppConfig. This directory is meant for configuration files that the user rarely touches directly.
  • Application data (databases, internal state files): Use AppData if the data should roam with the user’s profile on Windows; use AppLocalData for machine‑specific, non‑roaming data.
  • Cached assets (thumbnails, web responses): Use AppCache. The OS may purge this directory when disk space is low.
  • Log files: Use AppLog. On macOS this puts logs in ~/Library/Logs, which is consistent with Apple’s guidelines.
  • Temporary files: Use Temp. Assume the file may disappear after your application closes.

Distinguish AppConfig from AppData:

While AppConfig and AppData often resolve to the same folder on macOS and Windows, they are separate concepts on Linux (~/.config vs ~/.local/share). Always choose the semantic one — AppConfig for settings, AppData for internal application data — so your app behaves correctly on all platforms.

Common Mistakes When Using System Directories

Treating resourceDir as writable storage

The Resource directory points to assets bundled inside the application binary. On macOS and Linux it is sealed and cannot be written to. Attempting to write a file there will throw an OS error. Use AppData or AppLocalData for any writable data your application produces at runtime.

Assuming the directory already exists

While directories like Document and Download usually exist on the user’s system, app‑specific directories (AppData, AppCache, etc.) are created lazily. Always call mkdir with recursive: true before writing a file to an app‑specific directory.

Using absolute paths instead of BaseDirectory

Mixing path.join(await homeDir(), 'file.txt') with readTextFile('file.txt', { baseDir: BaseDirectory.Home }) within the same codebase is harmless but suggests confusion. The baseDir approach is more concise and avoids accidental path traversal issues; prefer it when using the fs plugin.

Forgetting that downloadDir points to the user’s downloads folder

On Windows, this is C:\Users\Name\Downloads. On macOS, ~/Downloads. On Linux, ~/Downloads (or a localized version). Storing temporary application data here will clutter the user’s download list. Reserve Download for user‑facing downloads only.

Hard‑coding file extensions or separators

When constructing relative paths for use with baseDir, always use forward slashes. On Windows the plugin handles the conversion internally. Manually inserting backslashes can break on non‑Windows builds.

Summary

System directories give you a single, declarative way to target well‑known filesystem locations without embedding platform‑specific paths. By using BaseDirectory with the path API and file system plugin, your application stores data in the right place on every OS while remaining inside the boundaries defined by your capability file.

The key to using them correctly is matching the directory’s purpose to your data: app‑specific configuration goes in AppConfig, user‑facing documents in Document, temporary files in Temp. When you get that right, you avoid common permission pitfalls and create a desktop application that feels native to the platform it runs on.

You will learn to compose, resolve, and manipulate paths safely so that every file operation — whether reading a bundled asset or writing a log — is predictable and correct across all targets.