Skip to main content
customizing-tauri-windows Customize Tauri desktop windows: custom titlebars, transparent windows, decorations, drag regions, menus, submenus, and menu keyboard shortcuts. USE WHEN building a frameless or custom titlebar, transparent window, drag region, or native window menu in Tauri.
インストールへ移動 Skills Marketplace コミュニティが作成したAIスキルを発見・探索
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
直接コマンドでは確認用 Prompt が省略されます。実行前にソースを確認してください。
npx skills add https://github.com/Sheshiyer/skill-clusters --skill customizing-tauri-windowsコマンドは1行のまま表示されます。コピー前に横へスクロールして全体を確認してください。
ローカルで確認しますか?SkillsMP が現在取得できるファイルをダウンロードできます。
Zipをダウンロード ダウンロード中... このリポジトリの他の Skills Arcplume runs Grok through the Grok Build CLI's own OAuth-authenticated session (grok login) for image generation, with strict preflight validation, secret-safe handling, and headless CLI-driven execution -- no separate XAI_API_KEY billing. Video falls back to the billed xAI API. USE WHEN a user wants to generate an image via a locally logged-in Grok Build CLI session, e.g. 'generate an image with grok', 'use grok build', or 'use my logged-in grok session'.
Shared reference for the Selemene cluster: the two report surfaces (deterministic Rust reports vs. narrative witness-pipeline readings), the @selemene/bridge CLI contract, the output manifest format, and non-prescriptive witnessing tone. USE WHEN deciding which Selemene surface to invoke or when routing between birth/compatibility/transit reports and solo/dyadic readings.
Route Selemene Engine tasks to the right surface: deterministic reports (birth/compatibility/transit) via selemene-core and the @selemene/bridge CLI, or narrative witness readings via selemene-report. USE WHEN the user wants anything under the Selemene/Noesis umbrella but has not named the exact surface.
name customizing-tauri-windows description Customize Tauri desktop windows: custom titlebars, transparent windows, decorations, drag regions, menus, submenus, and menu keyboard shortcuts. USE WHEN building a frameless or custom titlebar, transparent window, drag region, or native window menu in Tauri. cluster tauri version 1.0.0
Tauri Window Customization
Covers window customization in Tauri v2: custom titlebars, transparent windows, and window menus.
Configuration Methods
tauri.conf.json - Static configuration at build time
JavaScript Window API - Runtime modifications from frontend
Rust Window struct - Runtime modifications from backend
Window Configuration (tauri.conf.json)
{
"app" : {
"windows" : [ {
"title" : "My App" ,
"width" :
800
,
"height"
:
600
,
"decorations"
:
true
,
"transparent"
:
false
,
"alwaysOnTop"
:
false
,
"center"
:
true
}
]
}
}
Custom Titlebar Implementation
Step 1: Disable Decorations { "app" : { "windows" : [ { "decorations" : false } ] } }
Step 2: Configure Permissions (src-tauri/capabilities/default.json) {
"identifier" : "main-capability" ,
"windows" : [ "main" ] ,
"permissions" : [
"core:window:default" ,
"core:window:allow-start-dragging" ,
"core:window:allow-close" ,
"core:window:allow-minimize" ,
"core:window:allow-toggle-maximize"
]
}
Step 3: HTML Structure <div class ="titlebar" >
<div class ="titlebar-drag" data-tauri-drag-region >
<span class ="title" > My Application</span >
</div >
<div class ="titlebar-controls" >
<button id ="titlebar-minimize" > -</button >
<button id ="titlebar-maximize" > []</button >
<button id ="titlebar-close" > x</button >
</div >
</div >
<main class ="content" > </main >
Step 4: CSS Styling .titlebar {
height : 30px ;
background : #329ea3 ;
position : fixed;
top : 0 ;
left : 0 ;
right : 0 ;
display : grid;
grid-template-columns : 1 fr auto;
user-select : none;
}
.titlebar-drag {
display : flex;
align-items : center;
padding-left : 12px ;
}
.titlebar-controls { display : flex; }
.titlebar-controls button {
width : 46px ;
height : 30px ;
border : none;
background : transparent;
color : white;
cursor : pointer;
}
.titlebar-controls button :hover { background : rgba (255 ,255 ,255 ,0.1 ); }
.titlebar-controls button #titlebar-close :hover { background : #e81123 ; }
.content { margin-top : 30px ; padding : 16px ; }
Step 5: JavaScript Controls import { getCurrentWindow } from '@tauri-apps/api/window' ;
const appWindow = getCurrentWindow ();
document .getElementById ('titlebar-minimize' )
?.addEventListener ('click' , () => appWindow.minimize ());
document .getElementById ('titlebar-maximize' )
?.addEventListener ('click' , () => appWindow.toggleMaximize ());
document .getElementById ('titlebar-close' )
?.addEventListener ('click' , () => appWindow.close ());
Drag Region Behavior The data-tauri-drag-region attribute applies only to its element, not children. This preserves button interactivity. Add the attribute to each draggable child if needed.
Manual Drag with Double-Click Maximize document .getElementById ('titlebar' )?.addEventListener ('mousedown' , (e ) => {
if (e.buttons === 1 && e.target === e.currentTarget ) {
e.detail === 2 ? appWindow.toggleMaximize () : appWindow.startDragging ();
}
});
macOS Transparent Titlebar
Cargo.toml [target."cfg(target_os = \"macos\")".dependencies]
cocoa = "0.26"
Rust Implementation use tauri::{TitleBarStyle, WebviewUrl, WebviewWindowBuilder};
pub fn run () {
tauri::Builder::default ()
.setup (|app| {
let win_builder = WebviewWindowBuilder::new (app, "main" , WebviewUrl::default ())
.title ("Transparent Titlebar Window" )
.inner_size (800.0 , 600.0 );
#[cfg(target_os = "macos" )]
let win_builder = win_builder.title_bar_style (TitleBarStyle::Transparent);
let window = win_builder.build ().unwrap ();
#[cfg(target_os = "macos" )]
{
use cocoa::appkit::{NSColor, NSWindow};
use cocoa::base::{id, nil};
let ns_window = window.ns_window ().unwrap () as id;
unsafe {
let bg_color = NSColor::colorWithRed_green_blue_alpha_ (
nil, 50.0 /255.0 , 158.0 /255.0 , 163.5 /255.0 , 1.0
);
ns_window.setBackgroundColor_ (bg_color);
}
}
Ok (())
})
.run (tauri::generate_context!())
.expect ("error while running tauri application" )
}
Note : Custom titlebars on macOS lose native features like window snapping. Transparent titlebar preserves these.
Window Menus
Menu Item Types Type Description Text Basic labeled menu option Check Toggleable entry with checked state Separator Visual divider between sections Icon Entry with custom icon (Tauri 2.8.0+)
Creating Menus (JavaScript/TypeScript) import { Menu , MenuItem , Submenu , PredefinedMenuItem , CheckMenuItem } from '@tauri-apps/api/menu' ;
const fileSubmenu = await Submenu .new ({
text : 'File' ,
items : [
await MenuItem .new ({
id : 'new' , text : 'New' , accelerator : 'CmdOrCtrl+N' ,
action : () => console .log ('New' )
}),
await MenuItem .new ({
id : 'open' , text : 'Open' , accelerator : 'CmdOrCtrl+O' ,
action : () => console .log ('Open' )
}),
await MenuItem .new ({
id : 'save' , text : 'Save' , accelerator : 'CmdOrCtrl+S' ,
action : () => console .log ('Save' )
}),
{ type : 'Separator' },
await MenuItem .new ({
id : 'quit' , text : 'Quit' , accelerator : 'CmdOrCtrl+Q' ,
action : () => console .log ('Quit' )
})
]
});
const editSubmenu = await Submenu .new ({
text : 'Edit' ,
items : [
await PredefinedMenuItem .new ({ item : 'Undo' }),
await PredefinedMenuItem .new ({ item : 'Redo' }),
await PredefinedMenuItem .new ({ item : 'Separator' }),
await PredefinedMenuItem .new ({ item : 'Cut' }),
await PredefinedMenuItem .new ({ item : 'Copy' }),
await PredefinedMenuItem .new ({ item : 'Paste' })
]
});
const viewSubmenu = await Submenu .new ({
text : 'View' ,
items : [
await CheckMenuItem .new ({
id : 'sidebar' , text : 'Show Sidebar' , checked : true ,
action : async (item) => console .log ('Sidebar:' , await item.isChecked ())
})
]
});
const menu = await Menu .new ({ items : [fileSubmenu, editSubmenu, viewSubmenu] });
await menu.setAsAppMenu ();
Creating Menus (Rust) use tauri::menu::{MenuBuilder, SubmenuBuilder};
let file_menu = SubmenuBuilder::new (app, "File" )
.text ("new" , "New" )
.text ("open" , "Open" )
.text ("save" , "Save" )
.separator ()
.text ("quit" , "Quit" )
.build ()?;
let edit_menu = SubmenuBuilder::new (app, "Edit" )
.undo ()
.redo ()
.separator ()
.cut ()
.copy ()
.paste ()
.build ()?;
let menu = MenuBuilder::new (app)
.items (&[&file_menu, &edit_menu])
.build ()?;
app.set_menu (menu)?;
macOS Note : All menu items must be grouped under submenus. Top-level items are ignored.
Handling Menu Events (Rust) app.on_menu_event (|_app_handle, event| {
match event.id ().0 .as_str () {
"new" => println! ("New file" ),
"open" => println! ("Open file" ),
"save" => println! ("Save file" ),
"quit" => std::process::exit (0 ),
_ => {}
}
});
Dynamic Menu Updates const statusItem = await menu.get ('status' );
if (statusItem) await statusItem.setText ('Status: Ready' );
menu.get ("status" ).unwrap ().as_menuitem_unchecked ().set_text ("Status: Ready" )?;
Keyboard Shortcuts (Accelerators) Shortcut Accelerator String Ctrl+S / Cmd+S CmdOrCtrl+SCtrl+Shift+S CmdOrCtrl+Shift+SAlt+F4 Alt+F4F11 F11
Complete Example
main.rs use tauri::menu::{MenuBuilder, SubmenuBuilder};
pub fn run () {
tauri::Builder::default ()
.setup (|app| {
let file_menu = SubmenuBuilder::new (app, "File" )
.text ("new" , "New" )
.text ("open" , "Open" )
.separator ()
.text ("quit" , "Quit" )
.build ()?;
let edit_menu = SubmenuBuilder::new (app, "Edit" )
.undo ().redo ().separator ().cut ().copy ().paste ()
.build ()?;
let menu = MenuBuilder::new (app)
.items (&[&file_menu, &edit_menu])
.build ()?;
app.set_menu (menu)?;
Ok (())
})
.on_menu_event (|_app, event| {
match event.id ().0 .as_str () {
"quit" => std::process::exit (0 ),
id => println! ("Menu event: {}" , id),
}
})
.run (tauri::generate_context!())
.expect ("error while running tauri application" )
}
React Component import { useEffect } from 'react' ;
import { getCurrentWindow } from '@tauri-apps/api/window' ;
function App ( ) {
useEffect (() => {
const appWindow = getCurrentWindow ();
const minimize = ( ) => appWindow.minimize ();
const maximize = ( ) => appWindow.toggleMaximize ();
const close = ( ) => appWindow.close ();
document .getElementById ('titlebar-minimize' )?.addEventListener ('click' , minimize);
document .getElementById ('titlebar-maximize' )?.addEventListener ('click' , maximize);
document .getElementById ('titlebar-close' )?.addEventListener ('click' , close);
return () => {
document .getElementById ('titlebar-minimize' )?.removeEventListener ('click' , minimize);
document .getElementById ('titlebar-maximize' )?.removeEventListener ('click' , maximize);
document .getElementById ('titlebar-close' )?.removeEventListener ('click' , close);
};
}, []);
return (
<>
<div className ="titlebar" >
<div className ="titlebar-drag" data-tauri-drag-region >
<span > My Tauri App</span >
</div >
<div className ="titlebar-controls" >
<button id ="titlebar-minimize" > -</button >
<button id ="titlebar-maximize" > []</button >
<button id ="titlebar-close" > x</button >
</div >
</div >
<main className ="content" >
<h1 > Welcome to Tauri</h1 >
</main >
</>
);
}
export default App ;