tauri-v2 について
tauri-v2 は、Tauri v2+ でデスクトップアプリやモバイルアプリを開発するためのスキルです。Rust と Web フロントエンドの連携、権限設定、ビルドや実行時の問題の対処方法を扱います。
主な機能
- 01Tauri v2 のコマンドと IPC パターン
- 02ケイパビリティと権限設定のガイド
- 03Rust の状態管理とエラー処理
- 04デスクトップ・モバイルのビルド上の注意
- 05トラブルシューティング資料
活用シーン
- 01Tauri v2 アプリの構成を整える
- 02コマンド呼び出しや権限エラーを調査
- 03モバイルビルドの設定を準備
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ nodnarbnitram/ claude-code-extensions --skill tauri-v2インストール後、Claude Code で/tauri-v2を入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
README.mdreferences/advanced-runtime-reference.mdcapabilities-reference.mdipc-patterns.mdplugin-reference.mdREADME.mdupdater-distribution-reference.md詳細
Tauri v2+ 開発スキル
Web フロントエンドと Rust バックエンドで、クロスプラットフォームのデスクトップアプリとモバイルアプリを構築します。
始める前に
このスキルにより、よくある 8 件以上のエラーを防ぎ、トークンを約 60%節約できます。
| 指標 | スキルなし | スキルあり |
|---|---|---|
| セットアップ時間 | 約 2 時間 | 約 30 分 |
| よくあるエラー | 8 件以上 | 0 件 |
| トークン使用量 | 多い(調査が必要) | 少ない(直接的なパターン) |
このスキルで防げる既知の問題
- ケイパビリティの不足による権限拒否エラー
generate_handler!にコマンドが登録されていないことによる IPC の失敗- 型の不一致による状態管理のパニック
- Rust ターゲットの不足によるモバイルビルドの失敗
- 開発 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 に追加します |
| 本番環境でアップデーターが失敗する | 成果物が署名されていない、またはエンドポイントが HTTP | cargo tauri signer generate でキーを生成し、HTTPS エンドポイントのみを使用します |
| サイドカーが見つからない | tauri.conf.json に externalBin がない、または実行可能ファイルがない | bundle.externalBin にパスを追加し、バイナリがバンドルされていることを確認します |
| デスクトップでは動作するが、モバイルでは動作しない | デスクトップ専用 API を使用している | API がモバイルに対応しているか確認します。一部のプラグインはデスクトップ専用です |
| 状態へのアクセス時にパニックが発生する | State<T> の型が一致しない | .manage() で指定した型を正確に使用します |
| 起動時に画面が白くなる | フロントエンドがビルドされていない | 設定の beforeDevCommand を確認します |
| IPC がタイムアウトする | 非同期コマンドがブロックされている | ブロッキングコードを削除するか、spawn を使用します |
| モバイルビルドに失敗する | Rust ターゲットが不足している | rustup target add <target> を実行します |
詳細リファレンス
- セキュリティと権限 →
references/capabilities-reference.md - IPC の選択ガイド →
references/ipc-patterns.md - 公式プラグイン →
references/plugin-reference.md - アップデーターと配布 →
references/updater-distribution-reference.md - トレイ、サイドカー、ディープリンク →
references/advanced-runtime-reference.md
設定リファレンス
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/ にあります:
capabilities-reference.md- 権限のパターンと例ipc-patterns.md- IPC の完全な例plugin-reference.md- 公式プラグインのインストール、登録、権限文字列updater-distribution-reference.md- 署名、HTTPS の要件、バンドルの配布advanced-runtime-reference.md-TrayIconBuilder、サイドカー、ディープリンク、アセットプロトコル
注: 特定のトピックの詳細については、上記のリファレンスファイルを参照してください。
依存関係
必須
| パッケージ | バージョン | 用途 |
|---|---|---|
@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を参照してください。
公式ドキュメント
トラブルシューティング
起動時に白い画面が表示される
症状: アプリは起動しますが、画面が白紙のままです
解決策:
devUrlがフロントエンドの開発サーバーのポートと一致していることを確認しますbeforeDevCommandが開発サーバーを起動することを確認します- DevTools(Cmd+Option+I / Ctrl+Shift+I)を開いてエラーを確認します
コマンドが Undefined を返す
症状: invoke() が期待した値ではなく undefined を返します
解決策:
- コマンドが
generate_handler![]に含まれていることを確認します - Rust コマンドが実際に値を返していることを確認します
- 引数名が一致していることを確認します(デフォルトでは、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 を使用する前に、次の点を確認してください。
- プラグインのページを確認する:
v2.tauri.app/plugin/<name>/でプラットフォームの対応状況を確認します - デスクトップ専用の一般的な項目:システムトレイ(
TrayIconBuilder)、ウィンドウラベル/マルチウィンドウ、一部のシェルプラグイン機能 - モバイルでも安全なパターン:IPC コマンド/イベント/チャンネルはすべてのプラットフォームで動作します。
tauri::AppHandleはモバイルでも安全に使用できます - 条件付きコンパイル:プラットフォーム固有の 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 ターゲットがインストールされている