ウィンドウを表示・非表示にする

show()・hide()(権限 core:window:allow-show / allow-hide)でウィンドウを出し入れする。visible: false で起動時のチラつきを防ぐ手順と、隠したウィンドウを呼び戻す方法も示す。

ウィンドウ 対象: Tauri 2.x 更新日: 読了目安: 約9分 win-011
目次
  1. 前提条件
  2. 1. フロントエンドから実装する (TypeScript)
  3. 準備ができてから表示する
  4. 隠しておいたウィンドウを出し入れする
  5. 2. バックエンドから実装する (Rust)
  6. 動作確認
  7. よくあるエラーと対処法
  8. OS ごとの違いと注意点
  9. 関連レシピ

設定画面を閉じずに隠しておいて次は一瞬で出す、起動時は準備が整うまで見せない、ショートカットで出したり引っ込めたりする。こうした「出し入れ」は show() / hide() で行います。起動時の状態は tauri.conf.json の visible、実行中の切り替えは JS の show() / hide() か Rust の同名メソッドを使います(プラグイン不要)。気を付けたいのは、隠したウィンドウはタスクバーからも消え、ユーザーが自分では戻せなくなることです。戻す経路を用意してから隠すのが基本です。

前提条件

show() と hide() の権限(core:window:allow-show / core:window:allow-hide)は core:window:default に含まれないので追加します。状態を読む isVisible() / isFocused() は含まれています。表示したウィンドウを手前に出す setFocus() を使うなら core:window:allow-set-focus も足します。権限は呼び出す側のウィンドウで判定されるので、settings が自分を隠すなら settings も windows に入れます。

{
  "$schema": "../gen/schemas/desktop-schema.json",
  "identifier": "default",
  "description": "Capability for the main and settings windows",
  "windows": ["main", "settings"],
  "permissions": [
    "core:default",
    "core:window:allow-show",
    "core:window:allow-hide",
    "core:window:allow-set-focus"
  ]
}

起動時に見せないウィンドウには "visible": false を付けます。設定ファイルのウィンドウは起動時にすべて作られるので、隠れたままの settings もページの読み込みまで済み、あとは show() するだけで即座に出ます(その分のメモリは使います)。

{
  "app": {
    "windows": [
      { "label": "main", "title": "My App", "width": 1024, "height": 768, "visible": false },
      { "label": "settings", "title": "設定", "url": "settings.html", "width": 480, "height": 360, "visible": false }
    ]
  }
}

隠す・最小化する・閉じるの違いは次のとおりです。最小化の扱いは ウィンドウを最大化・最小化する で説明しています。

操作タスクバーのボタンユーザーが戻せるかページの状態
hide()消える戻せない(アプリ側で戻す)残る(JS も動き続ける)
minimize()残るクリックで戻せる残る
close()消える作り直しが必要破棄される

1. フロントエンドから実装する (TypeScript)

準備ができてから表示する

既定の visible: true では、ウィンドウが先に出て、読み込みと初期化が終わるまで白い画面や組み立て途中の画面が見えます。visible: false にしておき、初期化が済んだら show() します。finally で呼ぶのが要点で、初期化の例外で show() まで届かないと、画面が一度も出ないままプロセスだけが動き続けます。DevTools も開けないので原因を追いにくくなります。

import { getCurrentWindow } from '@tauri-apps/api/window';

async function initApp() {
  // 設定の読み込みや最初の描画など、見せる前に済ませたい処理
}

window.addEventListener('DOMContentLoaded', async () => {
  try {
    await initApp();
  } catch (e) {
    console.error('init failed:', e);
  } finally {
    await getCurrentWindow().show(); // core:window:allow-show が必要
  }
});

隠しておいたウィンドウを出し入れする

事前に作ったウィンドウは Window.getByLabel() で取得します。表示中でも他のウィンドウの裏にあることがあるので、出すときは show() に続けて setFocus() を呼びます(詳しくは ウィンドウにフォーカスを当てる・外す)。閉じる代わりに隠せば、入力途中の内容も残ります。

import { getCurrentWindow, Window } from '@tauri-apps/api/window';

// main の「設定」ボタンから呼ぶ
export async function openSettings() {
  const settings = await Window.getByLabel('settings');
  if (!settings) throw new Error('settings window not found');
  await settings.show();
  await settings.setFocus(); // 裏に隠れていても手前に出す
}

// settings.html の「閉じる」ボタンから呼ぶ(破棄せずに隠す)
export async function hideSelf() {
  await getCurrentWindow().hide();
}

× ボタンで閉じる代わりに隠す方法は、トレイ常駐と合わせて 最小化ボタンでトレイに格納するようにする で扱います。

2. バックエンドから実装する (Rust)

Rust の show() / hide() は capability の設定なしで呼べます。次の例は 3 つの処理をまとめたものです。

  • 読み込みが終わったら表示: on_page_load はすべてのウィンドウの読み込みで呼ばれるので、ラベルと完了を確かめ、再読み込みで勝手に出ないよう最初の 1 回だけにします。JS が例外で止まっても必ず表示されます。非同期の初期化が長いなら 1 章の方法が向きます。
  • 切り替え: 「表示中かつフォーカスあり」なら隠し、それ以外(非表示・最小化・裏に隠れている)なら前面に出します(手順は win-012 と同じ)。グローバルショートカット から呼ぶ想定です。
  • macOS の Dock: ウィンドウをすべて隠してもアプリのアイコンは Dock に残り、クリックしても隠したウィンドウは戻りません。クリックで届く RunEvent::Reopen を受けて表示します。
use std::sync::atomic::{AtomicBool, Ordering};
use tauri::{webview::PageLoadEvent, AppHandle, Manager, WebviewWindow};

static MAIN_SHOWN: AtomicBool = AtomicBool::new(false);

/// 前面にあれば隠し、それ以外なら表示して前面に出す
fn toggle(window: &WebviewWindow) -> tauri::Result<()> {
    if window.is_visible()? && window.is_focused()? {
        window.hide()
    } else {
        window.show()?;
        if window.is_minimized()? {
            window.unminimize()?;
        }
        window.set_focus()
    }
}

#[tauri::command]
fn toggle_main(app: AppHandle) -> Result<(), String> {
    let main = app.get_webview_window("main").ok_or("main window not found")?;
    toggle(&main).map_err(|e| e.to_string())
}

#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
    let app = tauri::Builder::default()
        // tauri.conf.json で main に "visible": false を付けておく
        .on_page_load(|webview, payload| {
            if webview.label() == "main"
                && matches!(payload.event(), PageLoadEvent::Finished)
                && !MAIN_SHOWN.swap(true, Ordering::SeqCst)
            {
                let _ = webview.window().show();
            }
        })
        .invoke_handler(tauri::generate_handler![toggle_main])
        .build(tauri::generate_context!())
        .expect("error while building tauri application");

    app.run(|_app, _event| {
        // macOS: すべて隠した状態で Dock のアイコンがクリックされたら main を戻す
        #[cfg(target_os = "macos")]
        if let tauri::RunEvent::Reopen { has_visible_windows: false, .. } = _event {
            if let Some(main) = _app.get_webview_window("main") {
                let _ = main.show();
                let _ = main.set_focus();
            }
        }
    });
}

動作確認

npm run tauri dev で起動すると、読み込みが終わってから main が現れ、白い画面は見えなくなります。次の関数をボタンから呼ぶと、ウィンドウが消えて 3 秒後に戻ります。隠れている間もページの JS が動いていることが分かります。

import { getCurrentWindow } from '@tauri-apps/api/window';

export async function hideFor3s() {
  const win = getCurrentWindow();
  await win.hide();
  console.log('visible:', await win.isVisible());
  setTimeout(async () => {
    await win.show();
    console.log('visible:', await win.isVisible());
  }, 3000);
}
visible: false
visible: true

よくあるエラーと対処法

  • 「window.show not allowed. Permissions associated with this command: core:window:allow-show」: 権限の追加漏れです(リリースビルドでは「Command plugin:window|show not allowed by ACL」)。visible: false と組み合わせると、このエラーが見えないまま画面が出ません。起動しないように見えたら、いったん visible: true に戻して DevTools でエラーを確かめるか、2 章の Rust 側で表示します。
  • settings の「閉じる」だけ失敗する: 「window.hide not allowed on window "settings", ...」で始まるエラーなら、capability の windows に settings がありません。
  • 隠したウィンドウを戻せない: トレイ(menu-013)やショートカット(win-031)など戻す経路を必ず用意します。すべてのウィンドウを隠してもアプリは終了しないので、終了の手段も必要です。
  • Window.getByLabel('settings') が null を返す: 設定ファイルに settings がない、ラベルの綴り違い、または close() で破棄済みです。隠して使い回すウィンドウは close() しません。

OS ごとの違いと注意点

  • macOS: Dock から戻す処理(RunEvent::Reopen)を入れておきます。アプリ全体をまとめて隠す・戻す macOS 専用の hide() / show()(@tauri-apps/api/app、権限 core:app:allow-app-hide / core:app:allow-app-show)もあります。戻すときウィンドウにフォーカスは移らないので、必要なら setFocus() を続けます。
  • Windows / Linux: ウィンドウは見せたままタスクバーのボタンだけ消すなら、skipTaskbar: true か setSkipTaskbar()(権限 core:window:allow-set-skip-taskbar)を使います。macOS は非対応です。
  • 隠している間の処理: ページは生きていますが、隠れたページのタイマーは間引かれ、長く隠していると処理が止まることがあります。macOS 14 以降は設定の backgroundThrottling で変えられます(Windows・Linux は非対応)。隠したまま続ける定期処理は Rust 側に置くと確実です。
  • 注意を引きたいとき: 隠したウィンドウにはタスクバーのボタンがないので、点滅 させても Windows・Linux では目に入りません。表示中(最小化を含む)のときに使います。

関連レシピ

参考リンク(公式ドキュメント)

Web Ninja

この記事を書いた人

Web Ninja ウェブエンジニア (Web Engineer)

会社員ネットワークエンジニアから独立してかれこれ 25 年以上 Web エンジニアとして活動中。普段は JavaScript と Node.js を自在に操り、時には C++ や Perl といった古流の技も嗜みます。近年は Tauri × Rust という新たな武器を手に、デスクトップアプリ開発の最前線を駆け抜けています。「作りたい」を「作れる」に変えるための、実践的な「技」をお届けします。

お問い合わせ: tauri.ninja@gmail.com

内容の誤り・動かないコードを報告する