mcpField
T
tauri-v2

tauri-v2

github.com/nodnarbnitram開発168.1k 回インストール27 週間前に更新
リポジトリ

tauri-v2 について

tauri-v2 は、Tauri v2+ でデスクトップアプリやモバイルアプリを開発するためのスキルです。Rust と Web フロントエンドの連携、権限設定、ビルドや実行時の問題の対処方法を扱います。

主な機能

  1. 01Tauri v2 のコマンドと IPC パターン
  2. 02ケイパビリティと権限設定のガイド
  3. 03Rust の状態管理とエラー処理
  4. 04デスクトップ・モバイルのビルド上の注意
  5. 05トラブルシューティング資料

活用シーン

  1. 01Tauri v2 アプリの構成を整える
  2. 02コマンド呼び出しや権限エラーを調査
  3. 03モバイルビルドの設定を準備

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/nodnarbnitram/claude-code-extensions --skill tauri-v2

インストール後、Claude Code で/tauri-v2を入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

README.md
references/
advanced-runtime-reference.md
capabilities-reference.md
ipc-patterns.md
plugin-reference.md
README.md
updater-distribution-reference.md

詳細

Tauri v2+ 開発スキル

Web フロントエンドと Rust バックエンドで、クロスプラットフォームのデスクトップアプリとモバイルアプリを構築します。

始める前に

このスキルにより、よくある 8 件以上のエラーを防ぎ、トークンを約 60%節約できます。

指標スキルなしスキルあり
セットアップ時間約 2 時間約 30 分
よくあるエラー8 件以上0 件
トークン使用量多い(調査が必要)少ない(直接的なパターン)
このスキルで防げる既知の問題
  1. ケイパビリティの不足による権限拒否エラー
  2. generate_handler! にコマンドが登録されていないことによる IPC の失敗
  3. 型の不一致による状態管理のパニック
  4. Rust ターゲットの不足によるモバイルビルドの失敗
  5. 開発 URL の設定ミスによる画面が白くなる問題

クイックスタート

ステップ 1:Tauri コマンドを作成する
// src-tauri/src/lib.rs
#[tauri::command]
fn greet(name: String) -> String {
    format!("Hello, {}!", name)
}

#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
    tauri::Builder::default()
        .invoke_handler(tauri::generate_handler![greet])
        .run(tauri::generate_context!())
        .expect("error while running tauri application");
}

重要な理由: generate_handler![] に含まれていないコマンドは、フロントエンドから呼び出してもエラーなしで失敗します。

main.rs は薄く保ちます: src-tauri/src/main.rs は、処理を最小限にとどめたパススルーにします。アプリケーションのロジックはすべて lib.rs に記述します。

// src-tauri/src/main.rs
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
fn main() {
    app_lib::run();
}

モバイルビルドでは、Tauri が main() を mobile_entry_point に置き換えるため、この分割が必須です。

ステップ 2:フロントエンドから呼び出す
import { invoke } from '@tauri-apps/api/core';

const greeting = await invoke<string>('greet', { name: 'World' });
console.log(greeting); // "Hello, World!"

重要な理由: @tauri-apps/api/core を使用します(@tauri-apps/api/tauri ではありません。これは v1 の API です)。

ステップ 3:必要な権限を追加する
// src-tauri/capabilities/default.json
{
    "$schema": "../gen/schemas/desktop-schema.json",
    "identifier": "default",
    "windows": ["main"],
    "permissions": ["core:default"]
}

重要な理由: Tauri v2 では、デフォルトですべての操作が拒否されます。すべての操作に明示的な権限が必要です。

重要なルール

必ず行うこと
  • すべてのコマンドを tauri::generate_handler![cmd1, cmd2, ...] に登録します
  • 適切なエラー処理のため、コマンドから Result<T, E> を返します
  • 複数のコマンドからアクセスする共有状態には Mutex<T> を使用します
  • プラグインの機能を使う前にケイパビリティを追加します
  • 共有コードには lib.rs を使用します(モバイルビルドで必須)
  • モバイルとの互換性のため、lib.rs の pub fn run() に #[cfg_attr(mobile, tauri::mobile_entry_point)] を付けます
絶対に行わないこと
  • 非同期コマンドでは借用型(&str)を使用せず、所有型を使用します
  • メインスレッドをブロックしません。I/O 操作には非同期処理を使用します
  • パスをハードコードせず、Tauri のパス API(app.path())を使用します
  • ケイパビリティの設定を省略しません。「安全な」操作にも権限が必要です
よくある間違い

誤り — 非同期処理で借用型を使用:

#[tauri::command]
async fn bad(name: &str) -> String { // Compile error!
    name.to_string()
}

正しい — 所有型を使用:

#[tauri::command]
async fn good(name: String) -> String {
    name
}

理由: 非同期コマンドでは await の実行箇所をまたいでデータを借用できません。Tauri では、非同期コマンドのパラメーターに所有型が必要です。

既知の問題の予防

問題根本原因解決策
「コマンドが見つからない」generate_handler! への登録漏れコマンドをハンドラーマクロに追加します
「権限が拒否された」ケイパビリティの不足capabilities/default.json に追加します
プラグインの機能が何も起こらず失敗するプラグインはインストール済みだが、ケイパビリティに権限がないプラグインの権限文字列を capabilities/default.json に追加します
本番環境でアップデーターが失敗する成果物が署名されていない、またはエンドポイントが HTTPcargo tauri signer generate でキーを生成し、HTTPS エンドポイントのみを使用します
サイドカーが見つからないtauri.conf.json に externalBin がない、または実行可能ファイルがないbundle.externalBin にパスを追加し、バイナリがバンドルされていることを確認します
デスクトップでは動作するが、モバイルでは動作しないデスクトップ専用 API を使用しているAPI がモバイルに対応しているか確認します。一部のプラグインはデスクトップ専用です
状態へのアクセス時にパニックが発生するState<T> の型が一致しない.manage() で指定した型を正確に使用します
起動時に画面が白くなるフロントエンドがビルドされていない設定の beforeDevCommand を確認します
IPC がタイムアウトする非同期コマンドがブロックされているブロッキングコードを削除するか、spawn を使用します
モバイルビルドに失敗するRust ターゲットが不足しているrustup target add <target> を実行します

詳細リファレンス

設定リファレンス

tauri.conf.json
{
    "$schema": "./gen/schemas/desktop-schema.json",
    "productName": "my-app",
    "version": "1.0.0",
    "identifier": "com.example.myapp",
    "build": {
        "devUrl": "http://localhost:5173",
        "frontendDist": "../dist",
        "beforeDevCommand": "npm run dev",
        "beforeBuildCommand": "npm run build"
    },
    "app": {
        "windows": [{
            "label": "main",
            "title": "My App",
            "width": 800,
            "height": 600
        }],
        "security": {
            "csp": "default-src 'self'; img-src 'self' data:",
            "capabilities": ["default"]
        }
    },
    "bundle": {
        "active": true,
        "targets": "all",
        "icon": ["icons/icon.icns", "icons/icon.ico", "icons/icon.png"]
    }
}

主な設定:

  • build.devUrl:フロントエンドの開発サーバーのポートと一致している必要があります
  • app.security.capabilities:ケイパビリティファイル識別子の配列です

プラグインの設定 — 一部のプラグインでは、追加の tauri.conf.json ブロック(例:store、updater)が必要です。必要な設定キーは、必ず v2.tauri.app/plugin/<plugin-name>/ にある各プラグインのドキュメントで確認してください。

プロジェクト構造

my-tauri-app/
├── src/                    # Frontend source
├── src-tauri/
│   ├── src/
│   │   ├── main.rs         # Thin passthrough — calls lib::run()
│   │   └── lib.rs          # ALL application logic lives here
│   ├── capabilities/
│   │   └── default.json    # Capability definitions (grant permissions here)
│   ├── tauri.conf.json     # App configuration (devUrl, bundle, security)
│   ├── Cargo.toml          # Rust dependencies
│   └── build.rs            # Build script (required for tauri-build)
└── package.json

すべてのロジックを lib.rs に置く理由: Tauri はモバイルでは main() を #[cfg_attr(mobile, tauri::mobile_entry_point)] に置き換えます。すべてのコマンド、状態、ビルダーの設定は lib.rs::run() に置く必要があります。

Cargo.toml
[package]
name = "app"
version = "0.1.0"
edition = "2021"

[lib]
name = "app_lib"
crate-type = ["staticlib", "cdylib", "rlib"]

[build-dependencies]
tauri-build = { version = "2", features = [] }

[dependencies]
tauri = { version = "2", features = [] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"

主な設定:

  • [lib] セクション:モバイルビルドで必須です
  • crate-type:クロスプラットフォーム対応には、3 種類すべてを含める必要があります

よく使われるパターン

エラー処理パターン

IPC 境界を越えて型安全にエラーを伝播するには、Result<T, E> と thiserror を使用します。実装の詳細については、references/ipc-patterns.md を参照してください。

use thiserror::Error;

#[derive(Debug, Error)]
enum AppError {
    #[error("IO error: {0}")]
    Io(#[from] std::io::Error),
    #[error("Not found: {0}")]
    NotFound(String),
}

impl serde::Serialize for AppError {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where S: serde::ser::Serializer {
        serializer.serialize_str(self.to_string().as_ref())
    }
}

#[tauri::command]
fn risky_operation() -> Result<String, AppError> {
    Ok("success".into())
}
Serde 境界のルール

すべてのコマンド引数は serde::Deserialize を実装し、戻り値の型は serde::Serialize を実装する必要があります。これにより、Tauri は IPC 境界を越えて JSON をやり取りします。

use serde::{Deserialize, Serialize};

#[derive(Deserialize)]
struct CreateUserArgs {
    name: String,
    email: String,
    role: Option<String>,  // Optional fields use Option<T>
}

#[derive(Serialize)]
struct User {
    id: u64,
    name: String,
}

#[tauri::command]
fn create_user(args: CreateUserArgs) -> Result<User, String> {
    Ok(User { id: 1, name: args.name })
}

よくある serde の落とし穴:

  • JS ではフィールド名が camelCase、Rust では snake_case です。Tauri が自動的に変換します
  • Option<T> は省略可能な JS 引数に対応します(undefined または null を指定できます)
  • 複雑な列挙型を JSON で安全に扱うには、#[serde(tag = "type")] などが必要です
  • エラー型も Serialize を実装する必要があります(上記のエラー処理パターンを参照してください)
状態管理パターン

Tauri の状態管理では、コマンド間でアプリケーションデータを管理します。より複雑な状態管理パターンについては、references/ipc-patterns.md を参照してください。

use std::sync::Mutex;
use tauri::State;

struct AppState {
    counter: u32,
}

#[tauri::command]
fn increment(state: State<'_, Mutex<AppState>>) -> u32 {
    let mut s = state.lock().unwrap();
    s.counter += 1;
    s.counter
}

// In builder:
tauri::Builder::default()
    .manage(Mutex::new(AppState { counter: 0 }))
イベント発行パターン

イベントは、送信後に応答を待たない通知です。双方向の例については、references/ipc-patterns.md を参照してください。

use tauri::Emitter;

#[tauri::command]
fn start_task(app: tauri::AppHandle) {
    std::thread::spawn(move || {
        app.emit("task-progress", 50).unwrap();
        app.emit("task-complete", "done").unwrap();
    });
}
import { listen } from '@tauri-apps/api/event';

const unlisten = await listen('task-progress', (e) => {
    console.log('Progress:', e.payload);
});
// Call unlisten() when done
チャネルストリーミングパターン

チャネルを使うと、Rust からフロントエンドへ、高頻度で型付けされたストリーミングを行えます。実装の詳細については、references/ipc-patterns.md を参照してください。

use tauri::ipc::Channel;

#[derive(Clone, serde::Serialize)]
#[serde(tag = "event", content = "data")]
enum DownloadEvent {
    Progress { percent: u32 },
    Complete { path: String },
}

#[tauri::command]
async fn download(url: String, on_event: Channel<DownloadEvent>) {
    for i in 0..=100 {
        on_event.send(DownloadEvent::Progress { percent: i }).unwrap();
    }
    on_event.send(DownloadEvent::Complete { path: "/downloads/file".into() }).unwrap();
}
import { invoke, Channel } from '@tauri-apps/api/core';

const channel = new Channel<DownloadEvent>();
channel.onmessage = (msg) => console.log(msg.event, msg.data);
await invoke('download', { url: 'https://...', onEvent: channel });
ウィンドウアクセスパターン

Tauri v2 では、ウィンドウと WebView の管理を統一するために WebviewWindow を使用します。

use tauri::Manager;

#[tauri::command]
fn focus_window(app: tauri::AppHandle) {
    if let Some(window) = app.get_webview_window("main") {
        let _ = window.set_focus();
    }
}

重要な理由: v2 では tauri::WebviewWindow と app.get_webview_window("label") を使用してください。v1 の app.get_window() API は v2 で削除されています。

バンドルされているリソース

リファレンス

references/ にあります:

注: 特定のトピックの詳細については、上記のリファレンスファイルを参照してください。

依存関係

必須
パッケージバージョン用途
@tauri-apps/cli^2 (v2+)CLI ツール
@tauri-apps/api^2 (v2+)フロントエンド API
tauri^2 (v2+)Rust コア
tauri-build^2 (v2+)ビルドスクリプト

*最終確認日:2026-04-02。機能の提供時期については、必ず公式変更履歴を確認してください。

任意(プラグイン)
パッケージバージョン用途主な権限
tauri-plugin-fs^2 (v2+)ファイルシステムへのアクセスfs:default
tauri-plugin-dialog^2 (v2+)ネイティブダイアログdialog:default
tauri-plugin-shell^2 (v2+)シェルコマンド、URL を開くshell:default
tauri-plugin-http^2 (v2+)HTTP クライアントhttp:default
tauri-plugin-store^2 (v2+)キーと値のストレージstore:default

プラグインの権限設定は必須です。 プラグインをインストールしても、権限文字列をケイパビリティファイルに追加しないと、実行時にエラーが通知されず失敗します。すべての公式プラグインのインストールと権限の詳細については、references/plugin-reference.mdを参照してください。

公式ドキュメント

トラブルシューティング

起動時に白い画面が表示される

症状: アプリは起動しますが、画面が白紙のままです

解決策:

  1. devUrl がフロントエンドの開発サーバーのポートと一致していることを確認します
  2. beforeDevCommand が開発サーバーを起動することを確認します
  3. DevTools(Cmd+Option+I / Ctrl+Shift+I)を開いてエラーを確認します
コマンドが Undefined を返す

症状: invoke() が期待した値ではなく undefined を返します

解決策:

  1. コマンドが generate_handler![] に含まれていることを確認します
  2. Rust コマンドが実際に値を返していることを確認します
  3. 引数名が一致していることを確認します(デフォルトでは、JS は camelCase、Rust は snake_case)
モバイルビルドの失敗

症状: Android/iOS のビルドがターゲット不足で失敗します

解決策:

# Android targets
rustup target add aarch64-linux-android armv7-linux-androideabi i686-linux-android x86_64-linux-android

# iOS targets (macOS only)
rustup target add aarch64-apple-ios x86_64-apple-ios aarch64-apple-ios-sim
デスクトップとモバイルの動作の違い

すべての Tauri API とプラグインがモバイル(iOS/Android)に対応しているわけではありません。モバイルビルドでプラグインや API を使用する前に、次の点を確認してください。

  1. プラグインのページを確認する:v2.tauri.app/plugin/<name>/ でプラットフォームの対応状況を確認します
  2. デスクトップ専用の一般的な項目:システムトレイ(TrayIconBuilder)、ウィンドウラベル/マルチウィンドウ、一部のシェルプラグイン機能
  3. モバイルでも安全なパターン:IPC コマンド/イベント/チャンネルはすべてのプラットフォームで動作します。tauri::AppHandle はモバイルでも安全に使用できます
  4. 条件付きコンパイル:プラットフォーム固有の Rust ロジックには #[cfg(desktop)] / #[cfg(mobile)] を使用します
#[tauri::command]
fn platform_info() -> String {
    #[cfg(desktop)]
    return "desktop".to_string();
    #[cfg(mobile)]
    return "mobile".to_string();
}

セットアップチェックリスト

このスキルを使用する前に、以下を確認してください。

  • npx tauri info に正しい Tauri v2 のバージョンが表示される
  • src-tauri/capabilities/default.json が存在し、少なくとも core:default が含まれている
  • すべてのコマンドが generate_handler![] に登録されている
  • lib.rs に共有コードが含まれている(モバイル対応のため)
  • 対象プラットフォームに必要な Rust ターゲットがインストールされている

コメント