Skip to Content
Examples13 · Tauri

13 · Tauri

Tauri builds the desktop shell in Rust, but the frontend is a web app with its own JS toolchain, usually Vite plus Jest. Here 3va fills that role with a single binary: dev server, bundler and test runner.

Source: examples/13-tauri

index.html loads /src/main.ts as a module src/ main.ts UI: volume slider, theme and mute buttons state.ts typed "commands" + reducer (mirrors #[tauri::command]) state.test.ts 7 tests package.json dev / build / test scripts pointing at 3va

This example contains only the frontend. There’s no src-tauri/ folder.

Dev server

cd examples/13-tauri 3va dev --port 3000

Open http://localhost:3000  to get a volume slider, a mute button and a light/dark toggle. .ts files are transpiled per request and served as real ES modules:

GET /src/main.ts (served)
import { applyCommand, initialAppState } from "/src/state.ts"; const app = document.getElementById("app"); …

Production bundle

3va bundle src/main.ts -o dist/bundle.js --minify

Tests

3va test
✓ applyCommand > set-theme cambia el tema ✓ applyCommand > set-volume válido ✓ applyCommand > set-volume a 0 silencia ✓ applyCommand > set-volume inválido es rechazado y no muta el estado ✓ applyCommand > mute / unmute ✓ reduceCommands > aplica una secuencia en orden ✓ reduceCommands > un comando inválido detiene la escritura pero no rompe los anteriores Tests: 7 Passed: 7

Wiring it into Tauri v2

In a real project, point Tauri’s build hooks at 3va:

src-tauri/tauri.conf.json
{ "build": { "beforeDevCommand": "3va dev --port 3000", "beforeBuildCommand": "3va bundle src/main.ts -o dist/bundle.js --minify", "devUrl": "http://localhost:3000", "frontendDist": "../dist" } }

cargo tauri dev starts 3va dev, points the webview at devUrl, and reloads on save.

The package.json scripts wrap the same commands, and because 3va <name> falls back to package.json scripts, 3va build runs the bundle step:

package.json
"scripts": { "dev": "3va dev --port 3000", "build": "3va bundle src/main.ts -o dist/bundle.js --minify", "test": "3va test", "tauri": "cargo tauri dev" }

How it works

state.ts models the backend commands as a discriminated union and a reducer:

src/state.ts
export type VolumeChange = | { kind: "set-volume"; value: number } | { kind: "mute" } | { kind: "unmute" }; export function applyCommand(state: AppState, cmd: ThemeChange | VolumeChange): CommandResult

In a real app these would be invoke("set_volume", { value }) calls handled by Rust. Keeping a TypeScript version of the same contract lets you test the UI logic (validation, 0 → muted, rejecting 150) without compiling the shell or mocking IPC.

Things to keep in mind

  • frontendDist needs an index.html, not just bundle.js. 3va bundle writes only the JS file. The root index.html loads /src/main.ts for dev and isn’t copied. For production, add an HTML file to dist/ that loads bundle.js with a classic <script> after #app, because the bundle is not an ES module (see 08 · Bundler). Otherwise cargo tauri build has nothing to open.
  • The TS reducer is a copy of the Rust logic, not the logic itself. The tests prove the frontend’s model is consistent. They don’t prove the Rust command behaves the same way. Keep the two in sync (e.g. generate the TS types from Rust with ts-rs or specta) and still test the Rust side with cargo test.
  • CSP: 3va dev sends its own Content-Security-Policy header, and Tauri applies the CSP from tauri.conf.json in production. The example README suggests 3va dev --port 3000 --no-csp so the dev policy doesn’t get in the way of the webview. Make sure your production CSP in tauri.conf.json is the one you actually want.
  • 3va build and 3va dev are not the same kind of command. 3va dev is a built-in (built-ins always win over scripts), while 3va build runs the build script through the package manager, outside the sandbox. It ran without a prompt here only because package.json sets "no-prompt": true.
  • ⚠ Dependencies may be out of date — run 3va install appears because there’s a package.json and no node_modules. The example has no dependencies, so it’s harmless.
  • cargo tauri dev itself is Rust tooling. 3va doesn’t replace it.