C.W.K.
Stream
Lesson 05 of 07 · published

Tray, Menus, Notifications

~13 min · tauri, tray, menu, notification

Level 0Web Tourist
0 XP0/56 lessons0/13 achievements
0/100 XP to next level100 XP to go0% complete
"These are the touches that make a webview-powered app feel like it actually belongs on the desktop."

System Tray: Living in the Menu Bar

A tray icon (menu-bar item on macOS, system-tray on Windows/Linux) lets your app live quietly in the background and stay one click away. You build one in Rust with TrayIconBuilder, attach a menu, and handle clicks — show/hide the window, quick actions, quit. For utilities and always-available helpers, the tray is the difference between 'an app you open' and 'an app that's just there.'

Menus: Native Menu Bars and Context Menus

Tauri's menu API (Menu, Submenu, MenuItem, and the builder variants) constructs real native menus — the app menu bar at the top of the screen on macOS, context menus, and the tray's dropdown. Menu items emit events you handle in Rust. Native menus feel right because they are the OS's menus, not HTML pretending to be one.

Notifications: Tapping the User on the Shoulder

The notification plugin sends real OS notifications. Like the browser API, it's permission-gated: check isPermissionGranted, call requestPermission if needed, then sendNotification. Use it for genuinely notification-worthy events (a long job finished, a message arrived) — not for chatter. The OS notification center is shared real estate; a noisy app gets muted.

Code

A tray icon with a menu (Rust)·rust
use tauri::tray::TrayIconBuilder;
use tauri::menu::{Menu, MenuItem};

// In setup(): a tray icon with a small menu.
let quit = MenuItem::with_id(app, "quit", "Quit", true, None::<&str>)?;
let show = MenuItem::with_id(app, "show", "Show Window", true, None::<&str>)?;
let menu = Menu::with_items(app, &[&show, &quit])?;

TrayIconBuilder::new()
    .icon(app.default_window_icon().unwrap().clone())
    .menu(&menu)
    .on_menu_event(|app, event| match event.id.as_ref() {
        "quit" => app.exit(0),
        "show" => { if let Some(w) = app.get_webview_window("main") { let _ = w.show(); } }
        _ => {}
    })
    .build(app)?;
Permission-gated OS notifications (frontend)·tsx
import {
  isPermissionGranted, requestPermission, sendNotification,
} from "@tauri-apps/plugin-notification";

let granted = await isPermissionGranted();
if (!granted) granted = (await requestPermission()) === "granted";
if (granted) {
  sendNotification({ title: "Export complete", body: "Your file is ready." });
}

External links

Exercise

Add a system tray icon with a two-item menu: 'Show Window' and 'Quit'. Make Show bring your main window forward and Quit exit the app. Bonus: send an OS notification when a (faked) long task completes, gating it behind the permission check. You've added the native touches that separate a desktop app from a wrapped web page.
Hint
TrayIconBuilder in setup(), handle on_menu_event by id. For notifications: isPermissionGranted → requestPermission → sendNotification. Remember the notification capability permission (three-surface rule).

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.