6.4 KiB
Tauri + WASM Setup Guide
🐛 Problem
Symptom:
- ✅ WASM works in browser (
npm run dev) - ❌ WASM fails in Tauri (
npm run tauri:dev) - Error:
Cannot assign to read only property 'toString'
Root Cause: Tauri webview requires special configuration to load WASM files correctly.
✅ Solution: Vite Plugins for WASM
1. Install Required Plugins
pnpm add -D vite-plugin-wasm vite-plugin-top-level-await
Why these plugins?
vite-plugin-wasm: Handles WASM file loading and initializationvite-plugin-top-level-await: Enables top-level await (required by WASM)
2. Update vite.config.ts
import wasm from 'vite-plugin-wasm'
import topLevelAwait from 'vite-plugin-top-level-await'
export default defineConfig({
plugins: [
vue(),
tailwindcss(),
wasm(), // ✅ Add WASM support
topLevelAwait(), // ✅ Add top-level await support
// ... other plugins
],
// Rest of config...
})
3. Tauri CSP Configuration
File: src-tauri/tauri.conf.json
Ensure CSP includes:
{
"security": {
"csp": {
"script-src": "'self' 'unsafe-inline' 'unsafe-eval' 'wasm-unsafe-eval'"
}
}
}
Key directive: 'wasm-unsafe-eval' is REQUIRED for WASM in Tauri 2.x
🎯 How It Works
Before (Without Plugins)
Tauri loads index.html
↓
Vite bundles JS (WASM as regular asset)
↓
Browser tries to load WASM
↓
💥 WASM initialization fails
↓
Error: Cannot assign to read only property
Why it fails:
- Vite doesn't know how to bundle WASM for Tauri
- WASM file is treated as regular asset
- Tauri webview can't initialize WASM correctly
After (With Plugins)
Tauri loads index.html
↓
vite-plugin-wasm handles WASM bundling
↓
WASM file served with correct headers
↓
vite-plugin-top-level-await enables async init
↓
✅ WASM loads successfully
Why it works:
vite-plugin-wasmhandles WASM as special asset type- Correct MIME type (
application/wasm) - Proper initialization order
- Compatible with Tauri's security model
📊 Comparison
| Aspect | Browser (dev) | Tauri (without plugins) | Tauri (with plugins) |
|---|---|---|---|
| WASM Loading | ✅ Works | ❌ Fails | ✅ Works |
| MIME Type | Auto | ❌ Wrong | ✅ Correct |
| Initialization | ✅ Success | ❌ Conflict | ✅ Success |
| CSP Compatibility | N/A | ❌ Issues | ✅ Compatible |
🔍 Debugging
Check if WASM is Loading
In Browser DevTools (F12 in Tauri window):
-
Network Tab:
Look for: neptune_wasm_bg.wasm Status: 200 OK Type: application/wasm -
Console Tab:
Should see: ✅ WASM initialized successfully Should NOT see: ❌ WASM init error -
Sources Tab:
Check if WASM file is listed under "webpack://" or "(no domain)"
Common Issues
Issue 1: WASM file not found (404)
Cause: WASM not bundled correctly
Fix: Ensure vite-plugin-wasm is installed and configured
Issue 2: CSP violation
Cause: Missing 'wasm-unsafe-eval' in CSP
Fix: Add to script-src in tauri.conf.json
Issue 3: Module initialization error
Cause: Top-level await not supported
Fix: Install vite-plugin-top-level-await
🧪 Testing Steps
1. Test in Browser (Should Work)
npm run dev
- Open http://localhost:5173
- Navigate to
/auth→ Create Wallet - Check console: Should see "✅ WASM initialized successfully"
2. Test in Tauri (Now Should Work)
npm run tauri:dev
- Tauri window opens
- Navigate to
/auth→ Create Wallet - Open DevTools (F12)
- Check console: Should see "✅ WASM initialized successfully"
- Should NOT see any
toStringerrors
3. Test Wallet Generation
// In CreateWalletFlow.vue
const { generateWallet } = useNeptuneWallet()
// Click "Create Wallet" button
const result = await generateWallet()
// Should return:
{
receiver_identifier: "...",
seed_phrase: ["word1", "word2", ..., "word18"]
}
📦 Package Versions
Installed:
{
"devDependencies": {
"vite-plugin-wasm": "^3.5.0",
"vite-plugin-top-level-await": "^1.6.0"
}
}
Compatible with:
- Vite 7.x
- Tauri 2.x
- Vue 3.x
🔧 Configuration Files
vite.config.ts
import wasm from 'vite-plugin-wasm'
import topLevelAwait from 'vite-plugin-top-level-await'
export default defineConfig({
plugins: [
vue(),
wasm(),
topLevelAwait(),
// ... other plugins
],
})
tauri.conf.json
{
"app": {
"security": {
"csp": {
"script-src": "'self' 'unsafe-inline' 'unsafe-eval' 'wasm-unsafe-eval'"
}
}
}
}
package.json
{
"dependencies": {
"@neptune/wasm": "file:./packages/neptune-wasm"
},
"devDependencies": {
"vite-plugin-wasm": "^3.5.0",
"vite-plugin-top-level-await": "^1.6.0"
}
}
💡 Why Browser Works But Tauri Doesn't
Browser (Chrome/Firefox)
- Permissive WASM loading: Browsers automatically handle WASM
- Built-in support: No special config needed
- Dev server: Vite dev server serves WASM with correct headers
Tauri (Webview)
- Strict security: CSP enforced by default
- Custom protocol: Assets loaded via
tauri://protocol - WASM restrictions: Requires
'wasm-unsafe-eval'in CSP - Asset handling: Needs proper Vite configuration
Tauri = Embedded Browser + Rust Backend
- More secure (CSP enforced)
- More restrictive (needs explicit config)
- Different asset loading (custom protocol)
🚀 Result
Before:
npm run dev # ✅ WASM works
npm run tauri:dev # ❌ WASM fails (toString error)
After:
npm run dev # ✅ WASM works
npm run tauri:dev # ✅ WASM works! 🎉
📚 Resources
- vite-plugin-wasm GitHub
- vite-plugin-top-level-await GitHub
- Tauri Security Documentation
- WebAssembly with Vite
🎯 Summary
Problem: Tauri can't load WASM without proper Vite configuration
Solution: Install vite-plugin-wasm + vite-plugin-top-level-await
Result: WASM works in both browser AND Tauri! 🚀