Skip to content
Tauri

Embedding Additional Files

You may need to include additional files in your application bundle that aren’t part of your frontend (your frontendDist) directly or which are too big to be inlined into the binary. We call these files resources.

To bundle the files of your choice, add the resources property to the bundle object in your tauri.conf.json file.

To include a list of files:

tauri.conf.json
{
"bundle": {
"resources": [
"./path/to/some-file.txt",
"/absolute/path/to/textfile.txt",
"../relative/path/to/jsonfile.json",
"some-folder/",
"resources/**/*.md"
]
}
}

The bundled files will be in $RESOURCES/ with the original directory structure preserved, for example: ./path/to/some-file.txt -> $RESOURCE/path/to/some-file.txt

To fine control where the files will get copied to, use a map instead:

tauri.conf.json
{
"bundle": {
"resources": {
"/absolute/path/to/textfile.txt": "resources/textfile.txt",
"relative/path/to/jsonfile.json": "resources/jsonfile.json",
"resources/": "",
"docs/**/*md": "website-docs/"
}
}
}

To learn about where $RESOURCE resolves to on each platforms, see the documentation of resource_dir

Source path syntax

In the following explanations “target resource directory” is either the value after the colon in the object notation, or a reconstruction of the original file paths in the array notation.

  • "dir/file.txt": copies the file.txt file into the target resource directory.
  • "dir/": copies all files and directories recursively into the target resource directory. Use this if you also want to preserve the file system structure of your files and directories.
  • "dir/*": copies all files in the dir directory non-recursively (sub-directories will be ignored) into the target resource directory.
  • "dir/**: throws an error because ** only matches directories and therefore no files can be found.
  • "dir/**/*": copies all files in the dir directory recursively (all files in dir/ and all files in all sub-directories) into the target resource directory.
  • "dir/**/**: throws an error because ** only matches directories and therefore no files can be found.

To resolve the path for a resource file, instead of manually calculating the path, use the following APIs

On the Rust side, you need an instance of the PathResolver which you can get from App and AppHandle, then call PathResolver::resolve:

tauri::Builder::default()
.setup(|app| {
let resource_path = app.path().resolve("lang/de.json", BaseDirectory::Resource)?;
Ok(())
})

To use it in a command:

#[tauri::command]
fn hello(handle: tauri::AppHandle) {
let resource_path = handle.path().resolve("lang/de.json", BaseDirectory::Resource)?;
}

The path in the API calls can be either a normal relative path like folder/json_file.json that resolves to $RESOURCE/folder/json_file.json, or a paths like ../relative/folder/toml_file.toml that resolves to $RESOURCE/_up_/relative/folder/toml_file.toml, these APIs use the same rules as you write tauri.conf.json > bundle > resources, for example:

tauri.conf.json
{
"bundle": {
"resources": ["folder/json_file.json", "../relative/folder/toml_file.toml"]
}
}
let json_path = app.path().resolve("folder/json_file.json", BaseDirectory::Resource)?;
let toml_path = app.path().resolve("../relative/folder/toml_file.toml", BaseDirectory::Resource)?;

Currently the resources are stored in the APK as assets so the return value of those APIs are not normal file system paths, we use a special URI prefix asset://localhost/ here that can be used with the fs plugin, with that, you can read the files through FsExt::fs like this:

let resource_path = app.path().resolve("lang/de.json", BaseDirectory::Resource).unwrap();
let json = app.fs().read_to_string(&resource_path);

If you want or must have the resource files to be on a real file system, copy the contents out manually through the fs plugin

In this example we want to bundle additional i18n json files like this:

.
├── src-tauri/
│ ├── tauri.conf.json
│ ├── lang/
│ │ ├── de.json
│ │ └── en.json
│ └── ...
└── ...
tauri.conf.json
{
"bundle": {
"resources": ["lang/*"]
}
}
lang/de.json
{
"hello": "Guten Tag!",
"bye": "Auf Wiedersehen!"
}

On the Rust side, you need an instance of the PathResolver which you can get from App and AppHandle:

tauri::Builder::default()
.setup(|app| {
// The path specified must follow the same syntax as defined in
// `tauri.conf.json > bundle > resources`
let resource_path = app.path().resolve("lang/de.json", BaseDirectory::Resource)?;
let json = std::fs::read_to_string(&resource_path).unwrap();
// Or when dealing with Android, use the file system plugin instead
// let json = app.fs().read_to_string(&resource_path);
let lang_de: serde_json::Value = serde_json::from_str(json).unwrap();
// This will print 'Guten Tag!' to the terminal
println!("{}", lang_de.get("hello").unwrap());
Ok(())
})
#[tauri::command]
fn hello(handle: tauri::AppHandle) -> String {
let resource_path = handle.path().resolve("lang/de.json", BaseDirectory::Resource)?;
let json = std::fs::read_to_string(&resource_path).unwrap();
// Or when dealing with Android, use the file system plugin instead
// let json = handle.fs().read_to_string(&resource_path);
let lang_de: serde_json::Value = serde_json::from_str(json).unwrap();
lang_de.get("hello").unwrap()
}

For the JavaScript side, you can either use a command like the one above and call it through await invoke('hello') or access the files using the fs plugin.

When using the fs plugin, in addition to the basic setup, you’ll also need to configure the access control list to enable any plugin APIs you need as well as the permissions to access the $RESOURCE folder:

src-tauri/capabilities/default.json
{
"$schema": "../gen/schemas/desktop-schema.json",
"identifier": "main-capability",
"description": "Capability for the main window",
"windows": ["main"],
"permissions": [
"core:default",
"fs:allow-read-text-file",
"fs:allow-resource-read-recursive"
]
}
import { resolveResource } from '@tauri-apps/api/path';
import { readTextFile } from '@tauri-apps/plugin-fs';
const resourcePath = await resolveResource('lang/de.json');
const langDe = JSON.parse(await readTextFile(resourcePath));
console.log(langDe.hello); // This will print 'Guten Tag!' to the devtools console

Since we replace ../ to _up_ in relative paths and the root to _root_ in abosolute paths when using a list, those files will be in sub folders inside the resource directory, to allow those paths in Tauri’s permission system, use $RESOURCE/**/* to allow recursive access to those files

With a file bundled like this:

tauri.conf.json
{
"bundle": {
"resources": ["../relative/path/to/jsonfile.json"]
}
}

To use it with the fs plugin:

src-tauri/capabilities/default.json
{
"$schema": "../gen/schemas/desktop-schema.json",
"identifier": "main-capability",
"description": "Capability for the main window",
"windows": ["main"],
"permissions": [
"core:default",
"fs:allow-stat",
"fs:allow-read-text-file",
"fs:allow-resource-read-recursive",
{
"identifier": "fs:scope",
"allow": ["$RESOURCE/**/*"],
"deny": ["$RESOURCE/secret.txt"]
}
]
}

To use it with the opener plugin:

src-tauri/capabilities/default.json
{
"$schema": "../gen/schemas/desktop-schema.json",
"identifier": "main-capability",
"description": "Capability for the main window",
"windows": ["main"],
"permissions": [
"core:default",
{
"identifier": "opener:allow-open-path",
"allow": [
{
"path": "$RESOURCE/**/*"
}
]
}
]
}

© 2025 Tauri Contributors. CC-BY / MIT