Build a Menu Bar Timer in Tauri 2 | System Tray Plugin Capstone
About this lesson
A menu-bar app is a different animal from a regular desktop window. There is no dock icon and no big main window — just a small icon in the system tray that, when clicked, pops up a little panel. Hit the same icon again and the panel hides. Quit from a tray menu, not from a window close button. Tauri 2 has direct support for it via tauri::tray. Source code: https://github.com/GoCelesteAI/tauri_tomato This is the capstone of the Tauri Patterns for Production series. We build Tomato, a Pomodoro timer that lives in the menu bar. Click the tray icon, a 320 by 360 panel slides in with a 25:00 countdown and Start, Pause, Reset buttons. The session ticks down, an OS notification fires when it ends, and a daily-completions counter persists to disk. Every prior episode shows up somewhere: capabilities + bundle config (Ep 1), plugin-notification (Ep 3), WebviewWindow show/hide (Ep 4), plugin-store (Ep 5), Mutex AppState (Ep 7), and the CI matrix (Ep 9) still ships this app to three platforms without changes. What You'll Learn: - Menu-bar window config — visible: false, decorations: false, transparent: true, alwaysOnTop: true, skipTaskbar: true — five flags that turn a Tauri window into a tray popup. - TrayIconBuilder in the setup hook — registering the tray icon, attaching a right-click menu, handling left-click events. - show_menu_on_left_click(false) — left-click toggles your popup; right-click opens the menu. Standard menu-bar UX. - Mutex around TimerState — three coupled fields (running, started_at, remaining_secs) guarded by one Mutex, the same pattern from Episode 7. - Polling via setInterval calling invoke('tick') — simpler than events for a clock that updates twice a second. - app.notification().builder().title(...).body(...).show() — fire-and-forget OS notification when the session ends. - tauri-plugin-store for the daily counter — auto-save, JSON file on disk, survives quits and relaunches. - The capability permissions menu-bar apps need — core:window:allow-show
DeepCamp AI