Use Cases (Mis à jour: 02/06/2026)

Developpement Tauri avec Claude Code : Vite, commandes Rust et permissions

Guide pratique pour creer une app Tauri v2 avec Claude Code, Vite, Rust, invoke, permissions fichiers, tests et revue.

Developpement Tauri avec Claude Code : Vite, commandes Rust et permissions

Tauri permet de transformer une interface web en application desktop pour Windows, macOS et Linux. L’interface peut rester en React, Svelte ou un autre framework frontend, tandis que les operations proches du systeme vivent cote Rust. Cette separation est ideale avec Claude Code : on peut demander une modification d’UI, une commande Rust, une capability ou un test sans produire un diff trop large.

Ce guide utilise Tauri v2 et une petite application de notes locales comme exemple. Il explique quand choisir Tauri, comment demarrer avec Vite + React ou Svelte, comment ecrire des commandes Rust, comment appeler ces commandes avec invoke, comment limiter les permissions de fichiers, quels checks lancer, et quels prompts utiliser pour faire relire les permissions par Claude Code.

Les references officielles verifiees sont Tauri Create a Project, Calling Rust from the Frontend, Tauri Capabilities, File System plugin, Vite Getting Started, Cargo test et Claude Code setup. Pour renforcer la partie Rust, lisez Rust avec Claude Code. Pour comparer avec Electron, voyez Electron desktop app development.

Quand choisir Tauri

Tauri est pertinent quand l’application doit etre locale tout en gardant une experience de developpement web. Exemples : notes privees, convertisseur CSV interne, visionneuse de logs, outil developpeur, application offline pour terrain ou inventaire. Ces cas ont en commun des fichiers locaux, parfois un usage hors ligne, et un besoin de distribution desktop.

Tauri n’est pas le bon choix uniquement parce qu’il est leger. Il ajoute aussi une toolchain Rust, du packaging, de la signature, des installateurs, des differences OS, un identifiant d’application et une strategie de mise a jour. Si le produit est surtout une web app avec login et APIs distantes, une app web classique ou une PWA peut etre plus simple.

Commencez avec Claude Code par un prompt de cadrage :

Nous construisons une app Tauri v2 de notes locales.
N'implemente rien pour l'instant.
Decoupe le travail en React UI, commandes Rust, capabilities et build/test.
L'acces fichier doit rester dans le repertoire app data.
Ne propose pas de lecture ou ecriture de chemins arbitraires.

Ce prompt force la discussion sur les limites avant les permissions.

Limite d’architecture

Dans Tauri, le frontend ne touche pas directement le systeme. Il appelle des commandes Rust avec invoke; Rust valide l’entree, touche la ressource locale et renvoie un resultat type.

flowchart LR
  UI["React ou Svelte UI"] --> Invoke["invoke from @tauri-apps/api/core"]
  Invoke --> Command["Commande Rust"]
  Command --> Guard["allowlist et validation de chemin"]
  Guard --> AppData["repertoire app data"]
  Command --> Result["resultat type"]
  Result --> UI
  Capability["Tauri capability"] --> UI

Point important : les capabilities Tauri limitent l’acces du frontend aux APIs et plugins Tauri, mais elles ne rendent pas automatiquement vos commandes Rust sures. Si une commande Rust accepte un chemin arbitraire et ecrit dedans, elle reste dangereuse. Il faut donc relire les capabilities et la validation de chemin cote Rust.

Setup React ou Svelte

Pour un nouveau projet, le chemin officiel create-tauri-app est le plus propre. Choisissez React ou Svelte, puis TypeScript.

npm create tauri-app@latest taskdesk
cd taskdesk
npm install
npm run tauri dev

Pour un projet Vite existant, creez ou gardez le frontend puis initialisez Tauri. La documentation Vite indique npm create vite@latest et demande actuellement Node.js 20.19+ ou 22.12+, donc verifiez Node avant d’analyser les erreurs Tauri.

node --version
npm create vite@latest taskdesk -- --template react-ts
cd taskdesk
npm install
npm install -D @tauri-apps/cli@latest
npm install @tauri-apps/api@latest
npx tauri init
npx tauri dev

Pour Svelte, changez seulement le template :

npm create vite@latest taskdesk -- --template svelte-ts

Apres generation, faites d’abord lire les fichiers par Claude Code :

Lis package.json, tauri.conf.json et src-tauri/src/lib.rs.
Resume la structure du projet et les scripts disponibles.
Ne modifie aucun fichier pour l'instant.

Les templates evoluent. Partir du projet genere est plus fiable que d’imposer une structure ancienne.

Configuration Tauri minimale

tauri.conf.json relie le serveur Vite, la sortie de build, la fenetre et la capability.

{
  "$schema": "https://schema.tauri.app/config/2",
  "productName": "TaskDesk",
  "version": "0.1.0",
  "identifier": "com.example.taskdesk",
  "build": {
    "beforeDevCommand": "npm run dev",
    "devUrl": "http://localhost:5173",
    "beforeBuildCommand": "npm run build",
    "frontendDist": "../dist"
  },
  "app": {
    "windows": [
      {
        "title": "TaskDesk",
        "width": 1000,
        "height": 700
      }
    ],
    "security": {
      "capabilities": ["main-capability"]
    }
  },
  "bundle": {
    "active": true,
    "targets": "all"
  }
}

Relisez toujours identifier, devUrl et frontendDist. Ces trois champs causent beaucoup de fausses pistes quand Claude Code corrige ensuite le mauvais endroit.

Commandes Rust

L’application de notes a besoin de lire, ecrire et lister. L’exemple suivant montre surtout la validation : refuser les chemins absolus, .. et toute sortie du dossier app data.

// src-tauri/src/note_commands.rs
use serde::Serialize;
use std::{
    fs,
    path::{Component, Path, PathBuf},
};
use tauri::{AppHandle, Manager};

#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub struct NoteFile {
    name: String,
    path: String,
    bytes: u64,
    is_dir: bool,
}

fn reject_unsafe_relative(path: &Path) -> Result<(), String> {
    for component in path.components() {
        match component {
            Component::Normal(_) | Component::CurDir => {}
            _ => return Err("use a relative path inside app data".to_string()),
        }
    }
    Ok(())
}

fn app_data_root(app: &AppHandle) -> Result<PathBuf, String> {
    let root = app
        .path()
        .app_data_dir()
        .map_err(|error| format!("failed to get app data dir: {error}"))?;
    fs::create_dir_all(&root).map_err(|error| format!("failed to create app data dir: {error}"))?;
    root.canonicalize()
        .map_err(|error| format!("failed to resolve app data dir: {error}"))
}

fn existing_path(app: &AppHandle, relative: &str) -> Result<PathBuf, String> {
    let root = app_data_root(app)?;
    let requested = Path::new(relative);
    reject_unsafe_relative(requested)?;
    let full = root
        .join(requested)
        .canonicalize()
        .map_err(|error| format!("path does not exist: {error}"))?;

    if !full.starts_with(&root) {
        return Err("path escapes app data".to_string());
    }

    Ok(full)
}

#[tauri::command]
pub fn read_note(app: AppHandle, path: String) -> Result<String, String> {
    let safe_path = existing_path(&app, &path)?;
    fs::read_to_string(safe_path).map_err(|error| format!("failed to read note: {error}"))
}

Enregistrez la commande dans lib.rs :

// src-tauri/src/lib.rs
mod note_commands;

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

Appel depuis TypeScript

Gardez un wrapper unique autour de invoke.

// src/lib/notesApi.ts
import { invoke } from "@tauri-apps/api/core";

export type NoteFile = {
  name: string;
  path: string;
  bytes: number;
  isDir: boolean;
};

export const notesApi = {
  read(path: string) {
    return invoke<string>("read_note", { path });
  },
  write(path: string, content: string) {
    return invoke<void>("write_note", { path, content });
  },
  list(dir = ".") {
    return invoke<NoteFile[]>("list_notes", { dir });
  },
};

Un premier ecran React peut rester tres petit :

import { useState } from "react";
import { notesApi } from "./lib/notesApi";

export default function App() {
  const [content, setContent] = useState("");
  const [message, setMessage] = useState("Ready");

  async function saveNote() {
    await notesApi.write("daily-note.txt", content);
    setMessage("Saved");
  }

  return (
    <main>
      <textarea value={content} onChange={(event) => setContent(event.target.value)} />
      <button onClick={saveNote}>Save</button>
      <p>{message}</p>
    </main>
  );
}

Permissions et capabilities

Si le frontend utilise directement le File System plugin, limitez la capability.

{
  "$schema": "../gen/schemas/desktop-schema.json",
  "identifier": "main-capability",
  "description": "Main window permissions for TaskDesk.",
  "windows": ["main"],
  "permissions": [
    "core:default",
    "fs:default",
    "fs:allow-app-read-recursive",
    "fs:allow-app-write-recursive"
  ]
}

Dans l’exemple de cet article, les fichiers passent par Rust. Il n’est donc pas necessaire de donner au frontend des permissions larges sur Downloads ou Documents. Prompt de revue :

Revue seulement, ne modifie rien.
Inspecte src-tauri/capabilities et src-tauri/src/note_commands.rs.
Liste chaque API exposee au frontend et chaque commande Rust.
Pour chacune, indique les chemins de fichiers accessibles.
Signale chemins absolus, .., wildcards larges et ecriture hors app data.
Propose le plus petit ensemble de permissions compatible avec la fonctionnalite.

Build, tests et usages

Separez les checks :

npm run build
cd src-tauri
cargo test
cd ..
npm run tauri build

npm run build couvre Vite, cargo test couvre Rust, puis npm run tauri build couvre le package desktop. Les regles de chemin se testent bien en Rust pur :

#[cfg(test)]
mod tests {
    use super::reject_unsafe_relative;
    use std::path::Path;

    #[test]
    fn rejects_parent_directory() {
        assert!(reject_unsafe_relative(Path::new("../secret.txt")).is_err());
    }
}

Trois usages tres concrets : notes locales, conversion CSV/Markdown, visionneuse de logs developpeur. Un quatrieme est l’app offline de terrain, par exemple inventaire, inspection ou check-in. Les pieges courants sont de confondre capability et securite Rust, de demander toute l’app en une fois a Claude Code, de garder la configuration de dev en production, de tester sur un seul OS, et d’ouvrir trop largement le systeme de fichiers.

CTA et resultat teste

Pour transformer cette approche en workflow d’equipe, consultez les produits et templates ClaudeCodeLab ou la page Claude Code training and consultation. Une vraie adoption doit inclure CLAUDE.md, prompts de revue, limites de permissions et commandes de verification.

Dans l’essai pratique derriere cet article, l’ordre le plus stable a ete : definir la limite de chemins en Rust, ajouter le wrapper TypeScript invoke, puis relire les capabilities. Commencer par l’UI donne une impression de vitesse, mais augmente souvent le retravail quand les regles de stockage deviennent claires.

#Claude Code #Tauri #Rust #desktop apps #frontend
Gratuit

PDF gratuit: cheatsheet Claude Code

Saisissez votre email et téléchargez une page avec commandes, habitudes de review et workflow sûr.

Nous protégeons vos données et n'envoyons pas de spam.

Masa

À propos de l'auteur

Masa

Ingénieur spécialisé dans les workflows pratiques avec Claude Code.