C.W.K.
Stream
Lesson 02 of 06 · published

AppHandle & the Manager Trait

~13 min · tauri, apphandle, manager, rust

Level 0Web Tourist
0 XP0/56 lessons0/13 achievements
0/100 XP to next level100 XP to go0% complete
"Inside a command, Tauri hands you what you need. Outside one — in setup, a thread, a timer — you carry an AppHandle to reach back into the app."

The Handle You Take With You

A command gets its State and Window injected, but plenty of code runs outside a command: the setup hook, a background thread, an async task, an OS event callback. In those places you hold an AppHandle — a cheap, clonable reference to the running application. Clone it, move it into a thread, and you can still emit events, read managed state, and find windows from anywhere.

The Manager Trait Is the Toolbox

Most of the useful methods — .state::<T>(), .emit(), .get_webview_window(), .path() for app directories — come from the Manager trait. App, AppHandle, Window, and WebviewWindow all implement it, so the same toolbox is available whether you're in setup with an &App or deep in a thread with an AppHandle. use tauri::Manager; is the import that lights those methods up.

Reaching State Without Injection

Inside a command you take State<T> as a parameter. Outside one, you fetch it from the handle: app.state::<T>() returns the same managed instance. This is how a background loop can read the same config a command reads, or how a setup hook can prime a cache. One managed value, two ways to reach it depending on where your code runs.

Code

Carry the AppHandle into a background thread·rust
use tauri::Manager;
use std::time::Duration;

pub fn run() {
    tauri::Builder::default()
        .manage(AppConfig { /* ... */ })
        .setup(|app| {
            // Clone the handle so a background thread can use the app.
            let handle = app.handle().clone();
            std::thread::spawn(move || {
                loop {
                    std::thread::sleep(Duration::from_secs(5));
                    // Reach managed state from outside a command:
                    let cfg = handle.state::<AppConfig>();
                    // Push news to the frontend (events: next lessons):
                    let _ = handle.emit("tick", cfg.theme.clone());
                }
            });
            Ok(())
        })
        .run(tauri::generate_context!())
        .expect("error while running app");
}

External links

Exercise

In your setup() hook, clone the AppHandle and spawn a std::thread that sleeps 3 seconds in a loop and prints something using handle (e.g. reads managed state and prints it). Confirm it runs in the background while your UI stays responsive. You're practicing the 'carry the handle out of the command world' pattern that events and timers depend on.
Hint
let handle = app.handle().clone(); then std::thread::spawn(move || { loop { sleep; let s = handle.state::<MyState>(); ... } }). The move keyword transfers the cloned handle into the thread.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.