Managing Tauri App Resources App Icons Icon Generation Generate all platform-specific icons from a single source file: cargo tauri icon
Default: ./app-icon.png
cargo tauri icon ./custom.png -o ./icons
Custom source/output
cargo tauri icon --ios-color "#000000"
iOS background color
Source requirements: Squared PNG or SVG with transparency. Generated Formats Format Platform icon.icns macOS icon.ico Windows .png Linux, Android, iOS Configuration { "bundle" : { "icon" : [ "icons/32x32.png" , "icons/128x128.png" , "icons/128x128@2x.png" , "icons/icon.icns" , "icons/icon.ico" ] } } Platform Requirements Windows (.ico): Layers for 16, 24, 32, 48, 64, 256 pixels. Android: No transparency. Place in src-tauri/gen/android/app/src/main/res/mipmap- folders. Each needs ic_launcher.png , ic_launcher_round.png , ic_launcher_foreground.png . iOS: No transparency. Place in src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/ . Required sizes: 20, 29, 40, 60, 76, 83.5 pixels with 1x/2x/3x scales, plus 512x512@2x. Embedding Static Resources Configuration Array syntax (preserves directory structure): { "bundle" : { "resources" : [ "./file.txt" , "folder/" , "docs//*.md" ] } } Map syntax (custom destinations): { "bundle" : { "resources" : { "path/to/source.json" : "resources/dest.json" , "docs//.md" : "website-docs/" } } } Path Patterns Pattern Behavior "dir/file.txt" Single file "dir/" Directory recursive "dir/" Files non-recursive "dir/*/" All files recursive Accessing Resources - Rust use tauri :: Manager ; use tauri :: path :: BaseDirectory ;
[tauri::command]
fn load_resource ( handle : tauri :: AppHandle ) -> Result < String , String
{ let path = handle . path ( ) . resolve ( "lang/de.json" , BaseDirectory :: Resource ) . map_err ( | e | e . to_string ( ) ) ? ; std :: fs :: read_to_string ( & path ) . map_err ( | e | e . to_string ( ) ) } Accessing Resources - JavaScript import { resolveResource } from '@tauri-apps/api/path' ; import { readTextFile } from '@tauri-apps/plugin-fs' ; const resourcePath = await resolveResource ( 'lang/de.json' ) ; const content = await readTextFile ( resourcePath ) ; const data = JSON . parse ( content ) ; Permissions { "permissions" : [ "fs:allow-read-text-file" , "fs:allow-resource-read-recursive" ] } Use $RESOURCE/*/ scope for recursive access. State Management Basic Setup use tauri :: { Builder , Manager } ; struct AppData { welcome_message : & 'static str , } fn main ( ) { Builder :: default ( ) . setup ( | app | { app . manage ( AppData { welcome_message : "Welcome!" , } ) ; Ok ( ( ) ) } ) . run ( tauri :: generate_context! ( ) ) . unwrap ( ) } Thread-Safe Mutable State use std :: sync :: Mutex ; use tauri :: { Builder , Manager } ;
[derive(Default)]
struct AppState { counter : u32 , } fn main ( ) { Builder :: default ( ) . setup ( | app | { app . manage ( Mutex :: new ( AppState :: default ( ) ) ) ; Ok ( ( ) ) } ) . run ( tauri :: generate_context! ( ) ) . unwrap ( ) } Accessing State in Commands use std :: sync :: Mutex ; use tauri :: State ;
[tauri::command]
fn increase_counter ( state : State < '_ , Mutex < AppState
) -> u32 { let mut state = state . lock ( ) . unwrap ( ) ; state . counter += 1 ; state . counter }
[tauri::command]
fn get_counter ( state : State < '_ , Mutex < AppState
) -> u32 { state . lock ( ) . unwrap ( ) . counter } Async Commands with Tokio Mutex use tokio :: sync :: Mutex ; use tauri :: State ;
[tauri::command]
async fn increase_counter_async ( state : State < '_ , Mutex < AppState
) -> Result < u32 , ( )
{ let mut state = state . lock ( ) . await ; state . counter += 1 ; Ok ( state . counter ) } Accessing State Outside Commands use std :: sync :: Mutex ; use tauri :: { Manager , Window , WindowEvent } ; fn on_window_event ( window : & Window , event : & WindowEvent ) { let app_handle = window . app_handle ( ) ; let state = app_handle . state :: < Mutex < AppState
( ) ; let mut state = state . lock ( ) . unwrap ( ) ; state . counter += 1 ; } Type Alias Pattern Prevent runtime panics from type mismatches: use std :: sync :: Mutex ; struct AppStateInner { counter : u32 , } type AppState = Mutex < AppStateInner
;
[tauri::command]
fn get_counter ( state : State < '_ , AppState
) -> u32 { state . lock ( ) . unwrap ( ) . counter } Multiple State Types use std :: sync :: Mutex ; use tauri :: { Builder , Manager , State } ; struct UserState { username : Option < String
} struct AppSettings { theme : String } fn main ( ) { Builder :: default ( ) . setup ( | app | { app . manage ( Mutex :: new ( UserState { username : None } ) ) ; app . manage ( Mutex :: new ( AppSettings { theme : "dark" . into ( ) } ) ) ; Ok ( ( ) ) } ) . run ( tauri :: generate_context! ( ) ) . unwrap ( ) }
[tauri::command]
fn login ( user_state : State < '_ , Mutex < UserState
, username : String ) { user_state . lock ( ) . unwrap ( ) . username = Some ( username ) ; }
[tauri::command]
fn set_theme ( settings : State < '_ , Mutex < AppSettings
, theme : String ) { settings . lock ( ) . unwrap ( ) . theme = theme ; } Key Points Arc not required - Tauri handles reference counting internally Use std::sync::Mutex for most cases; Tokio's mutex only for holding locks across await points Type safety - Wrong state types cause runtime panics, not compile errors; use type aliases Complete Example tauri.conf.json: { "bundle" : { "icon" : [ "icons/32x32.png" , "icons/128x128.png" , "icons/icon.icns" , "icons/icon.ico" ] , "resources" : { "assets/config.json" : "config.json" , "assets/translations/" : "lang/" } } } src-tauri/src/main.rs: use std :: sync :: Mutex ; use serde :: { Deserialize , Serialize } ; use tauri :: { Builder , Manager , State } ; use tauri :: path :: BaseDirectory ;
[derive(Default)]
struct AppState { counter : u32 , locale : String } type ManagedState = Mutex < AppState
;
[derive(Serialize, Deserialize)]
struct Config { app_name : String , version : String }
[tauri::command]
fn increment ( state : State < '_ , ManagedState
) -> u32 { let mut s = state . lock ( ) . unwrap ( ) ; s . counter += 1 ; s . counter }
[tauri::command]
fn load_config ( handle : tauri :: AppHandle ) -> Result < Config , String
{ let path = handle . path ( ) . resolve ( "config.json" , BaseDirectory :: Resource ) . map_err ( | e | e . to_string ( ) ) ? ; let content = std :: fs :: read_to_string ( & path ) . map_err ( | e | e . to_string ( ) ) ? ; serde_json :: from_str ( & content ) . map_err ( | e | e . to_string ( ) ) } fn main ( ) { Builder :: default ( ) . setup ( | app | { app . manage ( Mutex :: new ( AppState :: default ( ) ) ) ; Ok ( ( ) ) } ) . invoke_handler ( tauri :: generate_handler! [ increment , load_config ] ) . run ( tauri :: generate_context! ( ) ) . unwrap ( ) } Frontend: import { invoke } from '@tauri-apps/api/core' ; import { resolveResource } from '@tauri-apps/api/path' ; import { readTextFile } from '@tauri-apps/plugin-fs' ; const newValue = await invoke ( 'increment' ) ; const config = await invoke ( 'load_config' ) ; const langPath = await resolveResource ( 'lang/en.json' ) ; const translations = JSON . parse ( await readTextFile ( langPath ) ) ; Quick Reference Icon Commands cargo tauri icon
Generate from ./app-icon.png
cargo tauri icon ./icon.png -o out
Custom source/output
Resource Patterns { "resources" : [ "data.json" ] } // Single file { "resources" : [ "assets/" ] } // Directory recursive { "resources" : { "src/x.json" : "x.json" } } // Custom destination State Patterns app . manage ( Config { ... } ) ; // Immutable app . manage ( Mutex :: new ( State { ... } ) ) ; // Mutable fn cmd ( state : State < '_ , Mutex < T
) // In command app_handle . state :: < Mutex < T
( ) // Via AppHandle