Window Behavior Configuration

Configure how Tauri windows behave - resizability, fullscreen, always-on-top, focus, decorations, and all other runtime behavioral properties

The behavior of a Tauri window determines how it responds to user actions and the operating system. This includes whether the window can be resized, minimized, maximized, or closed, whether it stays on top of other windows, how it appears in the taskbar, and even what happens when a user tries to drag content into it. All of these are controlled by window behavior configuration options that you set either at launch time or adjust programmatically while the app runs.

Setting these properties correctly is what separates a polished desktop application from a web page that happens to live in a window. A kiosk app should not be resizable or closable; a chat overlay should stay on top; a video player needs to handle fullscreen transitions gracefully. This section covers every behavioral property Tauri v2 exposes, the effect each has on your window, and how to apply them from tauri.conf.json, from your React frontend via JavaScript, and from Rust.

How Window Behavior Is Configured

There are two distinct moments where you can influence window behavior: at window creation through static configuration, and after the window exists through runtime APIs.

Static configuration lives in tauri.conf.json under the app.windows array. These settings are read when Tauri creates the window and apply immediately. Many behavioral properties are boolean flags — resizable, maximizable, closable, fullscreen — and setting them here means the window is born with that behavior.

Runtime control uses the @tauri-apps/api/window package from your frontend or the tauri::Window struct in Rust. You can call methods like appWindow.setResizable(false) or appWindow.setAlwaysOnTop(true) while the app is running. This is how you implement features like a fullscreen toggle button or a "stay on top" checkbox.

Permissions Required:

Any runtime window operation requires the appropriate permission in your capability file. If you try to call setResizable without core:window:allow-set-resizable, the call will fail with a permissions error. You must explicitly list each permission you use.

The example below shows the minimum capability configuration for a window that will be controlled from the frontend:

src-tauri/capabilities/default.json
{
  "$schema": "../gen/schemas/desktop-schema.json",
  "identifier": "main-capability",
  "description": "Capability for the main window",
  "windows": ["main"],
  "permissions": [
    "core:window:default",
    "core:window:allow-close",
    "core:window:allow-minimize",
    "core:window:allow-toggle-maximize",
    "core:window:allow-set-resizable",
    "core:window:allow-set-fullscreen",
    "core:window:allow-set-focus",
    "core:window:allow-set-always-on-top",
    "core:window:allow-start-dragging"
  ]
}

The core:window:default permission includes allow-internal-toggle-maximize, which handles the internal maximize toggle logic. The remaining permissions unlock the specific APIs we need to adjust behavior from JavaScript.

Visibility and Initial Focus

A window's visibility and focus state control what the user sees when the app launches and how the window interacts with the rest of the desktop.

visible

When visible is true (the default), the window appears as soon as it is created. Setting it to false creates a hidden window that you can show later by calling appWindow.show() from JavaScript or window.show() from Rust. This is useful for splash screens that need to load data before revealing the UI, or for background windows that only appear when the user triggers them.

src-tauri/tauri.conf.json
{
  "app": {
    "windows": [
      {
        "label": "main",
        "visible": false
      }
    ]
  }
}

With this configuration, the app starts invisibly. On the React side, you might show the window after a data fetch completes:

src/App.tsx
import { getCurrentWindow } from "@tauri-apps/api/window";
function App() {
  const [ready, setReady] = useState(false);
  const appWindow = getCurrentWindow();
  useEffect(() => {
    async function init() {
      // Perform startup operations
      await loadConfig();
      setReady(true);
      await appWindow.show();
    }
    init();
  }, []);
  if (!ready) {
    return <div>Loading...</div>;
  }
  return <MainInterface />;
}

Don't forget the permission:

Calling show() requires core:window:allow-show. If you miss this, the window stays hidden with no error — the operation silently fails.

The window starts hidden, the loading screen renders, and only when the state changes to ready does show() make it appear. This prevents a flash of unstyled content or a half-loaded UI.

focus

When focus is true (the default), the new window grabs keyboard focus immediately. If you set it to false, the window opens but does not steal focus from whatever application the user was using. This matters for notification-style windows or background assistants that should appear without interrupting the user's workflow.

src-tauri/tauri.conf.json
{
  "app": {
    "windows": [
      {
        "label": "notifier",
        "focus": false,
        "alwaysOnTop": true
      }
    ]
  }
}

A notification window that pops up without stealing focus prevents the user from accidentally typing into the wrong place. They see it, but their cursor stays in their current application.

skipTaskbar

Setting skipTaskbar to true hides the window from the taskbar and alt-tab switcher. This is platform-dependent in its exact presentation, but the intent is the same across Windows, macOS, and Linux: the window exists but is not treated as a normal application window by the desktop environment.

Use this for floating tool panels, status indicators, or overlay windows that are tied to a parent window and should not clutter the taskbar.

visibleOnAllWorkspaces

This is a macOS-only property. When set to true, the window appears on every virtual desktop (every "Space"). It is ignored on other platforms.

Window Controls and Decorations

Window controls — the minimize, maximize, and close buttons — are part of what the operating system calls "decorations." Tauri lets you control each of these individually and also replace the entire titlebar with your own custom one.

resizable

When resizable is true (the default), the user can drag the window edges to change its size. Setting it to false locks the window dimensions to the initial width and height.

A non-resizable window is common for utility panels, login dialogs, or apps with a fixed layout that does not adapt to arbitrary sizes.

src-tauri/tauri.conf.json
{
  "app": {
    "windows": [
      {
        "label": "main",
        "resizable": false,
        "width": 400,
        "height": 300
      }
    ]
  }
}

You can also toggle resizability at runtime. The following React component provides a lock/unlock button:

src/components/ResizeToggle.tsx
import { getCurrentWindow } from "@tauri-apps/api/window";
import { useState } from "react";
function ResizeToggle() {
  const [locked, setLocked] = useState(true);
  const appWindow = getCurrentWindow();
  async function toggle() {
    const next = !locked;
    await appWindow.setResizable(next);
    setLocked(next);
  }
  return (
    <button onClick={toggle}>
      {locked ? "Unlock Size" : "Lock Size"}
    </button>
  );
}

Permission is mandatory for runtime toggles:

setResizable requires core:window:allow-set-resizable. Without it, the function will throw a permissions error. The same rule applies to every other set* method — each has a corresponding permission string you must add to the capability file.

maximizable, minimizable, closable

These three flags control whether the standard window control buttons are enabled. The default for all three is true.

  • maximizable: whether the maximize/restore button works.
  • minimizable: whether the minimize button works.
  • closable: whether the close button works and whether the operating system close action (Alt+F4, Cmd+Q) terminates the window.

Disabling closable does not prevent the window from being closed programmatically — it only removes the ability for the user to close it through the standard UI. A kiosk application, for example, would set closable to false and maximizable to false but keep minimizable true so the app can be tucked away without being exited.

Platform differences in disabled controls:

On macOS, the close button in the traffic-light controls may appear greyed out when closable is false, but the red button remains visible. On Windows, the close button may be removed entirely. Always test these appearance quirks on your target platforms.

maximized and fullscreen

Both maximized and fullscreen are startup state flags — they determine how the window is presented the moment it appears.

  • maximized: the window fills the available screen area (excluding the taskbar or menu bar) on launch.
  • fullscreen: the window enters a dedicated fullscreen space, covering the entire screen with no chrome visible.

These are false by default. Setting fullscreen to true is appropriate for media players or kiosk applications that must take over the display immediately.

You can transition into and out of both states at runtime using appWindow.toggleMaximize() and appWindow.setFullscreen(true/false).

src/components/FullscreenButton.tsx
import { getCurrentWindow } from "@tauri-apps/api/window";
function FullscreenButton() {
  const appWindow = getCurrentWindow();
  return (
    <button onClick={() => appWindow.setFullscreen(true)}>
      Enter Fullscreen
    </button>
  );
}

When the user presses Escape while in fullscreen, the browser's default behavior might interfere. You will typically need to listen for the Escape key and call appWindow.setFullscreen(false) to provide a consistent experience.

decorations

Setting decorations to false removes the native titlebar and window borders entirely. This is the prerequisite for building a custom titlebar. With decorations off, your HTML/CSS becomes the entire visible window chrome.

src-tauri/tauri.conf.json
{
  "app": {
    "windows": [
      {
        "label": "main",
        "decorations": false
      }
    ]
  }
}

A window without decorations cannot be dragged or resized by the user unless you provide alternative drag regions and resize handles. Tauri supports this through the data-tauri-drag-region HTML attribute.

<div data-tauri-drag-region class="titlebar">
  <span>My App</span>
  <button id="titlebar-minimize">-</button>
  <button id="titlebar-maximize"></button>
  <button id="titlebar-close">×</button>
</div>

Any element with data-tauri-drag-region becomes a drag handle — clicking and dragging it moves the window. The JavaScript to wire up the buttons uses the window API:

src/titlebar.ts
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());

A window without decorations needs explicit handling for all three actions, plus the drag behavior. For more complex drag behavior (like double-click to maximize), you can manually call appWindow.startDragging() on mousedown events.

Linux and decorations:

On Linux, especially under KDE with Wayland, removing decorations may result in the window being rendered without any server-side window frame, but in some setups the window manager will still apply a fallback decoration. As of Tauri v2, there is an open issue (tauri-apps/tauri#12955) about window decorations not respecting KDE's native appearance. If you target Linux, test custom titlebars across GNOME and KDE to ensure consistent behavior.

hiddenTitle and titleBarStyle

These are macOS-specific appearance controls.

  • hiddenTitle: when true, the window's title text is hidden while the traffic-light controls remain visible. The titlebar area shrinks, creating a cleaner look for apps that use their own header content.
  • titleBarStyle: can be "default", "transparent", or "overlay". "transparent" makes the titlebar background fully transparent while keeping the controls clickable. "overlay" places the traffic-light buttons over your content area.

Both require that decorations remain true. They are ignored on Windows and Linux.

Transparent titlebar on macOS:

To use titleBarStyle: "transparent" and set a custom background color from Rust, you must also set app.macOSPrivateApi to true in tauri.conf.json. This enables the private macOS API that Tauri needs to tint the titlebar background.

Layering and Window Order

Two properties control how the window sits in the Z-order relative to other applications.

alwaysOnTop

When true, the window stays above all other windows, even when another application is focused. This is the behavior of a screen ruler tool, a picture-in-picture player, or a persistent chat overlay.

src/hooks/useAlwaysOnTop.ts
import { getCurrentWindow } from "@tauri-apps/api/window";
import { useState } from "react";
export function useAlwaysOnTop() {
  const [isOnTop, setIsOnTop] = useState(false);
  const appWindow = getCurrentWindow();
  async function toggle() {
    await appWindow.setAlwaysOnTop(!isOnTop);
    setIsOnTop((prev) => !prev);
  }
  return { isOnTop, toggle };
}

The call appWindow.setAlwaysOnTop(true) requires core:window:allow-set-always-on-top.

alwaysOnBottom

The opposite: the window stays at the bottom of the Z-order, behind normal windows. This is rare but useful for desktop widgets that act as wallpaper overlays. Not all platforms guarantee the window remains at the absolute bottom if other applications also use this setting.

Drag and Drop Behavior

dragDropEnabled

Default true. When enabled, the user can drag files from their file manager and drop them onto the webview. The standard HTML drag-and-drop events fire just as they would in a browser.

Disable this if your application should not accept external files — for security in kiosk mode, or to prevent accidental file uploads in a data-sensitive environment.

src-tauri/tauri.conf.json
{
  "app": {
    "windows": [
      {
        "label": "secure-terminal",
        "dragDropEnabled": false
      }
    ]
  }
}

When drag-and-drop is disabled, the drop and dragover events will still fire in your JavaScript, but the actual file transfer is blocked at the OS level.

Content Protection and Privacy

contentProtected

When true, the window content is protected from being captured by screen recording or screenshot tools. This is partial support — the protection mechanism varies by platform, and determined capture methods may still bypass it. Use it to raise the bar for sensitive applications like password managers or medical record viewers.

incognito

Enabling incognito causes the webview to use an incognito/private browsing session. Local storage, cookies, and cache are not persisted to disk and are cleared when the window closes. This is useful for applications that handle temporary sessions or sensitive browsing.

Incognito does not equal security:

Incognito mode prevents local data persistence. It does not encrypt network traffic, prevent server-side tracking, or provide anonymity. It is strictly about the local browser storage lifecycle.

Transparency and Visual Effects

transparent

Setting transparent to true makes the window background fully transparent. Every pixel of your HTML/CSS body that is not painted will show through to the desktop behind it.

This requires additional setup. On macOS, you must set app.macOSPrivateApi to true in the configuration and, if using a custom background color, call into NSWindow via Rust. On Windows, transparency works if the system supports it (Windows 10 build 1903+). On Linux, compositor support varies.

src-tauri/tauri.conf.json
{
  "app": {
    "macOSPrivateApi": true,
    "windows": [
      {
        "label": "overlay",
        "transparent": true,
        "decorations": false
      }
    ]
  }
}

Performance with transparency:

Transparent windows with heavy animations or complex CSS can cause noticeable rendering lag because the compositor must blend every frame with the desktop below. Profile on your target hardware if you plan to use this for an always-on overlay.

windowEffects

This property applies platform-specific visual effects to the window background. The available values depend on the operating system and compositor.

On Windows 10/11, you can use "acrylic", "mica", or "tabbed". On macOS, "sidebar", "hudWindow", and others are available. These effects are applied through the OS compositor and give the window a frosted or tinted appearance.

src-tauri/tauri.conf.json
{
  "app": {
    "windows": [
      {
        "label": "sidebar",
        "windowEffects": "sidebar"
      }
    ]
  }
}

The effect is applied to the entire window background. If you combine this with transparent, the effect blends with the desktop, but the exact visual result is platform-specific and not always predictable.

shadow

Default true. Controls whether the operating system draws a drop shadow behind the window. Disable this for windows that should appear flat, such as panels that sit flush against the edge of the screen.

backgroundColor

A hex color string that sets the window's background color before the webview content renders. This prevents a flash of white or black while your application loads.

src-tauri/tauri.conf.json
{
  "app": {
    "windows": [
      {
        "label": "main",
        "backgroundColor": "#1e1e2e"
      }
    ]
  }
}

The value is only visible during the initial load and when the webview is empty. It does not affect the CSS background of your page.

Theme, Zoom Hotkeys, and Browser Settings

theme

Sets the initial color scheme preference for the webview. Accepted values are "light" and "dark". This determines which CSS media query prefers-color-scheme matches on first paint. The default is the system preference.

src-tauri/tauri.conf.json
{
  "app": {
    "windows": [
      {
        "label": "main",
        "theme": "dark"
      }
    ]
  }
}

You can change the theme at runtime by modifying the prefers-color-scheme via JavaScript injection or by setting the data-theme attribute on the document root and updating your CSS variables accordingly. The configuration property only sets the initial state.

zoomHotkeysEnabled

Default true. Enables the standard zoom keyboard shortcuts (Ctrl+/Cmd+ and Ctrl-/Cmd-) that change the page zoom level. Disable this in kiosk applications where users should not be able to modify the display scale.

userAgent

A custom user agent string that the webview sends with HTTP requests. This overrides the default browser user agent. Use it to identify your application to a backend service or to work around server-side user-agent parsing.

src-tauri/tauri.conf.json
{
  "app": {
    "windows": [
      {
        "label": "main",
        "userAgent": "MyTauriApp/1.0 (Windows; Desktop)"
      }
    ]
  }
}

browserExtensionsEnabled

Default false. When true, the webview supports loading browser extensions. This is primarily useful on desktop platforms that have a WebView2 or WebKit extension mechanism, but it is not widely tested across Tauri v2. Enable it only if you have a specific extension integration requirement.

Platform-Specific Behavioral Properties

Some properties only take effect on a single operating system. They are silently ignored on others.

acceptFirstMouse (macOS)

When true, the first mouse click on an inactive window is accepted as a regular click — not just used to focus the window. The macOS default is to use the first click only for focus. Set this if your application has controls that should respond immediately even when the window is not yet focused.

tabbingIdentifier (macOS)

Assigns a tabbing group identifier. macOS allows windows with the same tabbingIdentifier to be merged into a single tabbed window through the Window menu. This property is the string key that links them.

windowClassname (Windows)

A custom window class name. This can be used to apply specific styles or behaviors via Windows API hooks, or to identify the window for external automation tools.

Common Mistakes and How to Avoid Them

A few patterns cause problems often enough to call out directly.

Disabling closable without providing an alternative exit. If the user cannot close the window through the standard button, your application must provide a way to exit — a quit button in the UI or a menu action. Otherwise, the only way out is through the task manager.

Forgetting to add permissions when using runtime APIs. Every set* and toggle* method requires a specific permission string. The omission produces a runtime error, not a compile-time one, so you might not catch it until you test the specific button.

Assuming transparent works identically across platforms. Transparency rendering differs significantly between macOS (where it's smooth and reliable), Windows (where it requires DWM and specific Windows versions), and Linux (where the compositor decides). Always test on every target OS and provide fallback opaque backgrounds.

Combining decorations: false with resizable: true and no custom resize handles. With no titlebar and no resize handles, the user has no way to resize the window even though it is technically resizable. Either add CSS resize handles or set resizable to false.

A test checklist for every window configuration:

Before shipping, verify: (1) all runtime API calls have matching permissions, (2) custom titlebar buttons work consistently on all target platforms, (3) disabled controls produce the expected visual state, and (4) your configuration handles the case where a property is unsupported on a platform gracefully.

Summary

Window behavior configuration is the layer between your app's code and the operating system's window manager. The properties covered here — from resizable to windowEffects — define what the user can do with your window and how the window presents itself on the desktop.

The most important insight is that static configuration and runtime APIs serve different purposes. Use tauri.conf.json to set the initial state: a kiosk app starts fullscreen, a panel starts always-on-top, a setup wizard starts non-resizable. Use the JavaScript @tauri-apps/api/window methods to respond to user actions: toggling always-on-top, entering fullscreen on a button click, or showing a hidden window after loading completes.

The permission system ties them together. Every runtime method you call must be explicitly permitted in your capability file. This is a security feature that prevents malicious frontend code from manipulating windows without your knowledge, but it also means that missing a permission is the single most common source of "it doesn't work" bugs in Tauri window management.