Tauri v2 custom menu items
// Adding a webview navigation shortcut to the native app menu.
Iāve been trying to add a custom native Settings...
menu item to my Tauri v2 app. When clicking this menu item, I wanted the frontend webview to navigate to the /settings
page.
After a few days of struggling with this, Iāve finally got it working! Hereās how I did it:
Overview
In order to achieve this, we need to implement a custom menu via the Rust backend. This will allow us to add a custom menu item to our app menu with a specified id
.
Once added, we can listen for menu click events on the backend and emit
a window event to the frontend.
On the frontend, we can listen for this custom event and trigger the desired action which, in my case, will be navigating to the /settings
page.
Hereās what the code looks like:
Create a custom menu
In your main.rs
file, we will create a menu with various submenus as required (e.g. File, Edit, Help etc). From what I understand, menus can be created either globally or per window. I currently only have a single window in my app, so I opted for a global menu.
I added my custom Settings...
menu item to the App
submenu, which is the submenu that appears when you click on the app name in the top menu bar on macOS.
Once we have created the menu, we can use app.on_menu_event()
to listen for the menu item click event on the backend and emit
a browser window event to our frontend as follows:
use tauri::{
menu::{AboutMetadata, MenuBuilder, MenuItemBuilder, SubmenuBuilder},
Manager,
};
fn main() {
tauri::Builder::default()
.setup(|app| {
let handle = app.handle();
// my custom settings menu item
let settings = MenuItemBuilder::new("Settings...")
.id("settings")
.accelerator("CmdOrCtrl+,")
.build(app)?;
// my custom app submenu
let app_submenu = SubmenuBuilder::new(app, "App")
.about(Some(AboutMetadata {
..Default::default()
}))
.separator()
.item(&settings)
.separator()
.services()
.separator()
.hide()
.hide_others()
.quit()
.build()?;
// ... any other submenus
let menu = MenuBuilder::new(app)
.items(&[
&app_submenu,
// ... include references to any other submenus
])
.build()?;
// set the menu
app.set_menu(menu)?;
// listen for menu item click events
app.on_menu_event(move |app, event| {
if event.id() == settings.id() {
// emit a window event to the frontend
let _event = app.emit("custom-event", "/settings");
}
});
Ok(())
})
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
Listening for the custom event
On the frontend, we can listen for the custom-event
event using listen
function from the @tauri-apps/api/event
library.
Iām using Next.js
so hereās how I implemented this in my providers.tsx
component:
import React, { useEffect } from "react";
import { listen } from "@tauri-apps/api/event";
import { useRouter } from "next/navigation";
export default function Providers({ children }: { children: React.ReactNode }) {
const router = useRouter();
useEffect(() => {
// listen for Tauri events
const unlisten = listen("go-to", (e: { payload: string }) => {
console.log("An event occurred: ", e);
router.push(e.payload);
});
return () => {
if (unlisten === undefined) return;
unlisten.catch(console.error);
};
}, [router]);
}
If youāre getting __TAURI_IPC__ is not a function
errors, I would suggest checking your dependency versions.
Accelerator Key
We want our users to be able to navigate to the /settings
page using a hotkey as well by pressing CmdOrCtrl+,
on their keyboard. Tauri will take care of this for you, provided youāve set the accelerator
property when creating the menu item.
Thanks for reading! š«¶
I would love to hear your thoughts on this. If you have any questions or comments, please feel free to reach out on any of the platforms below. I look forward to connecting with you!