1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Tauri v2でサブウィンドウを開く

Last updated at Posted at 2025-08-31

1. やりたいこと

Tauri v2でWebviewWindowを使ってサブウィンドウを開きたい

2. 選択肢

以下の2つの方法があるようです

1. フロントエンドでWebviewWindowプラグインを使って実行する方法
2. バックエンドでWebviewWindowBuilderを使ってプラグインを自作する方法

普通にサブウィンドウを開くだけなら既存プラグインで十分なので、1の方法でやっていきます。また、フロントエンドはReactを選択します

3. 手順

  • プラグインのpermissionを設定する
  • 表示するビューのroutingを設定する
  • 呼び出し側のビューを作成する
  • サブウィンドウのビューを作成する

3-1. プラグインのpermissionを設定する

以下2つを追加します

  • 実行して良いウィンドウを追加する
    (windowsにsubを追加)
  • プラグインのpermissionを追加する
    (core:webviewプラグインのallow-create-webview-window機能の許可を追加)

デフォルトプロジェクトに追加すると以下のようになります

/src-tauri/capabilities/default.json
{
  "$schema": "../gen/schemas/desktop-schema.json",
  "identifier": "default",
  "description": "Capability for the main window",
  "windows": ["main", "sub"],
  "permissions": [
    "core:default",
    "opener:default",
    "core:webview:allow-create-webview-window"  # 追加
  ]
}

3-2. 表示するビューのroutingを設定する

React Routerを使うので環境にインストールします

terminal
npm i @tauri-apps/plugin-window @tauri-apps/plugin-dialog

/subにSubViewコンポーネントを割り当てます

/src/App.tsx
import { createHashRouter, RouterProvider } from "react-router";
import MainView from "./MainView";
import SubView from "./SubView";

const router = createHashRouter([
  { path: "/", element: <MainView /> },
  { path: "/sub", element: <SubView /> },
]);

export default function App() {
  return <RouterProvider router={router} />;
}

3-3. 呼び出し側のビューを作成する

サブウィンドウの呼び出しはnew WebviewWindow()により実行します。第1引数にウィンドウ名を書き、第2引数のurl:にWebViewのURLを書きます。使用するウィンドウ名"sub"が/src-tauri/capabilities/default.jsonのwindowsで許可されていて、React RouterによりWebViewのURLにビューが待機していれば正常に呼び出すことができます

/src/MainView.tsx
import { WebviewWindow } from "@tauri-apps/api/webviewWindow";

export default function MainView() {
  const openSubWindow = async () => {
    // 新しいウィンドウを開く
    const photo_selector = new WebviewWindow("sub", {
      url: "/#/sub",
      title: "設定",
      width: 900,
      height: 600,
      visible: true,
      resizable: true,
    });

    // 作成完了イベント
    photo_selector.once("tauri://created", () => {
      console.log("サブウィンドウが作成されました");
    });

    // エラーイベント
    photo_selector.once("tauri://error", (e) => {
      console.error("サブウィンドウ作成に失敗", e);
    });
  }


  return <div>
    <h1>メインウィンドウです</h1>
    <button onClick={openSubWindow}>クリックするとサブウィンドウを開きます</button>
  </div>
}

3-4. サブウィンドウのビューを作成する

呼び出されるビューは適当なコンポーネントで構いません

/src/SubView.tsx
export default function SubView() {
  return <h1>サブウィンドウです</h1>;
}

3-5. 完成したコード

この記事のコードが以下にあります
https://github.com/haneya-studio/tauri_subwindow_sample

4. 値をやりとりする

サブウィンドウとの間で値をやり取りしたくなることが多いと思います
2つやり方があるようなのでそれぞれ紹介します

4-1. URL経由で渡す

ReactRouterを使っているので、URLにオプションを付けて渡すことができます。Vueの場合もVueRouterを使えば同様にできます

/src/SubView.tsx
import { useLocation } from "react-router-dom";

export default function SubView() {
  const location = useLocation();
  const params = new URLSearchParams(location.search);

  const folder = params.get("folder");
  const mode = params.get("mode");

  return (
    <div>
      <h1>サブウィンドウ</h1>
      <p>フォルダ: {folder}</p>
      <p>モード: {mode}</p>
    </div>
  );
}

送る側はurlに付けて送るだけです

/src/MainView.tsx
  const photo_selector = new WebviewWindow("sub", {
    // url: "/#/sub",
    url: `/#/sub?folder=${encodeURIComponent(folder)}&mode=edit`,
    title: "設定",
    width: 1400,
    height: 800,
    visible: true,
    resizable: true,
  });    

4-2. イベント送受信で渡す

tauriのeventを使うと変数やオブジェクトを渡すことができます
emitToしたときにlistenしていればイベントを拾えるので、更新をかけたりも自在にできます

/src/SubView.tsxから送る
import { emitTo } from "@tauri-apps/api/event";

await emitTo("main", "addPhoto", path); // mainウィンドウのaddPhotoイベントにpathを渡す

受け取る側はlistenを置くだけでイベントを受け取ってくれますが、再レンダリングされたときに重複してリスナが作成されないようにuseEffectで作成します

/src/MainView.tsxで受け取る
import { listen, emitTo, type Event } from "@tauri-apps/api/event";

useEffect(() => {
  let active = true;
  let unlisten: (() => void) | undefined;

  (async () => {
    const stop = await listen<photoSelectorPayload>("addPhoto", (event: Event<photoSelectorPayload>) => {
      if (!active) return;
      if (event.payload.path) {
        console.log("受け取ったデータ:", event.payload.path);
      }
    });
    unlisten = stop;
  })();

  return () => {
    active = false;
    if (unlisten) unlisten();
  };
}, []);

まとめ

ググっても日本語の記事が出てこなかったので一応まとめておきます
レッツトライ

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?