Skilllibrary tauri-solidjs
Desktop application specialist for the Tauri + SolidJS stack — project scaffolding, IPC design, security configuration, build/distribution, and platform integration. Triggers: 'Tauri app', 'SolidJS desktop', 'Tauri invoke', 'tauri.conf.json', 'src-tauri', 'desktop app with SolidJS', 'Tauri commands', 'Tauri allowlist', 'Tauri v2 capabilities'. Do NOT use for web-only SolidJS apps (no Tauri), Electron apps, React Native/Flutter mobile apps, or pure Rust CLI tools without a frontend.
git clone https://github.com/merceralex397-collab/skilllibrary
T=$(mktemp -d) && git clone --depth=1 https://github.com/merceralex397-collab/skilllibrary "$T" && mkdir -p ~/.claude/skills && cp -r "$T/17-external-reference-seeds/tauri-solidjs" ~/.claude/skills/merceralex397-collab-skilllibrary-tauri-solidjs && rm -rf "$T"
17-external-reference-seeds/tauri-solidjs/SKILL.mdPurpose
Domain skill for building cross-platform desktop applications using the Tauri framework (Rust backend) with a SolidJS frontend. Covers project structure, IPC contract design, security model configuration, state management across the Rust–JS boundary, build pipelines, and platform-specific distribution (macOS, Windows, Linux).
When to use this skill
Use this skill when:
- scaffolding a new Tauri + SolidJS desktop application
- designing or implementing IPC contracts between SolidJS frontend and Rust backend (commands, events)
- configuring Tauri security: allowlists (v1), capabilities/permissions (v2), CSP headers
- building and distributing the app: platform bundles, auto-updater, code signing
- implementing desktop-specific features: system tray, file drag-and-drop, native menus, splash screens, deep linking, multi-window management
- managing state synchronization between SolidJS stores and Tauri's managed state
- troubleshooting Tauri build issues, WebView quirks, or platform-specific behavior
Do not use this skill when
- the app is a web-only SolidJS project with no desktop shell — use a SolidJS/web skill instead
- the desktop framework is Electron, Wails, or Neutralinojs — different architecture and APIs
- the task is a mobile app (React Native, Flutter, Capacitor) — even if it uses a webview
- the backend is pure Rust CLI with no frontend — use a Rust skill instead
- a quick one-off task unrelated to the Tauri+SolidJS stack — use
misc-helper
Operating procedure
1. Assess project state
- Check for existing project structure: does
exist? Is there asrc-tauri/
(v1) ortauri.conf.json
withtauri.conf.json
(v2)?capabilities - Identify Tauri version: v1 (
^1.x,@tauri-apps/api
crate ^1.x) vs v2 (tauri
^2.x,@tauri-apps/api
crate ^2.x). The security model, plugin system, and IPC patterns differ significantly.tauri - Check frontend setup: SolidJS (
), bundler (solid-js
withvite
), router (vite-plugin-solid
).@solidjs/router
2. Project structure (scaffold or validate)
my-app/ ├── src/ # SolidJS frontend │ ├── index.tsx # Entry point, render(<App />) │ ├── App.tsx # Root component with router │ ├── routes/ # Page components │ ├── components/ # Shared UI components │ ├── lib/ │ │ ├── tauri.ts # Typed wrappers around invoke() calls │ │ └── store.ts # SolidJS stores for app state │ └── index.html # HTML shell (loaded by WebView) ├── src-tauri/ │ ├── Cargo.toml # Rust dependencies │ ├── tauri.conf.json # Tauri configuration (window, security, bundle) │ ├── capabilities/ # (v2) Permission/capability definitions │ ├── src/ │ │ ├── main.rs # Tauri builder setup │ │ ├── commands/ # #[tauri::command] handlers │ │ ├── state.rs # Managed state (app_handle.manage()) │ │ └── lib.rs # Module declarations │ └── icons/ # App icons for all platforms ├── vite.config.ts # Vite + solid plugin + Tauri dev server config └── package.json
3. IPC contract design
Tauri Commands (frontend → backend):
- Define commands in Rust with
:#[tauri::command]#[tauri::command] async fn read_file(path: String) -> Result<String, String> { std::fs::read_to_string(&path).map_err(|e| e.to_string()) } - Register in the builder:
.invoke_handler(tauri::generate_handler![read_file]) - Call from SolidJS:
const content = await invoke<string>('read_file', { path: '/tmp/data.txt' }) - Always create typed wrappers in
to centralize invoke calls and provide TypeScript types.src/lib/tauri.ts
Event System (bidirectional):
- Backend → Frontend:
or window-scopedapp_handle.emit_all("event-name", payload)window.emit("event-name", payload) - Frontend → Backend:
withemit('event-name', payload)
in Rustapp.listen_global("event-name", handler) - Frontend listening:
— always unlisten on component cleanup.listen<PayloadType>('event-name', (event) => { ... })
4. Security configuration
Tauri v1 (allowlist):
- In
→tauri.conf.json
, enable only the APIs the app needs:tauri.allowlist{ "fs": { "scope": ["$APPDATA/*"], "readFile": true, "writeFile": true }, "dialog": { "open": true, "save": true }, "shell": { "open": true } } - Set restrictive CSP in
→tauri.conf.json
:tauri.security.csp"default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'"
Tauri v2 (capabilities):
- Define capabilities in
:src-tauri/capabilities/*.json{ "identifier": "main-window", "windows": ["main"], "permissions": ["fs:read", "fs:write", "dialog:open"] } - Use permission scoping to restrict file system access to specific directories.
- Never grant
orfs:*
— always scope to the minimum required.shell:*
5. SolidJS desktop-specific patterns
- Routing: use
fromHashRouter
(not@solidjs/router
) — there is no server to handle history-mode fallback in a desktop WebView.Router - File system access: always go through Tauri's
API or custom commands. Never use browserfs
.fetch('file://...') - Native menus: define in
→tauri.conf.json
or programmatically in Rust. Connect menu events to frontend via the event system.tauri.menu - State management: use SolidJS
/createStore
for UI state. For state that must persist or be shared with Rust, sync through Tauri commands or the event system. Avoid duplicating state across boundaries.createSignal
6. Build and distribution
- Development:
— starts Vite dev server + Tauri with hot reload.npm run tauri dev - Production build:
— produces platform-specific bundles:npm run tauri build- macOS:
and.dmg
bundle.app - Windows:
and.msi
(NSIS installer).exe - Linux:
,.deb
,.AppImage.rpm
- macOS:
- Code signing:
- macOS: set
,APPLE_CERTIFICATE
,APPLE_CERTIFICATE_PASSWORD
,APPLE_ID
env vars for notarization.APPLE_PASSWORD - Windows: set
for Authenticode signing.TAURI_SIGNING_PRIVATE_KEY
- macOS: set
- Auto-updater: configure
→tauri.conf.json
with endpoint URL, public key. The updater checks for new versions on app launch or on a timer.tauri.updater
7. Validate and deliver
- Test IPC contract: verify all commands are registered and callable from the frontend.
- Test on target platforms: WebView rendering can differ between macOS (WebKit), Windows (WebView2/Edge), and Linux (WebKitGTK).
- Verify bundle size: Tauri apps should be 5–15 MB. If significantly larger, check for unnecessary Rust dependencies or unoptimized assets.
Decision rules
- Tauri v2 over v1: for new projects, always use Tauri v2 unless the team has a specific v1 dependency. v2 has a better security model, mobile support, and plugin architecture.
- HashRouter always: never use history-mode routing in a Tauri WebView — it will break on refresh and deep links.
- Commands over events for request/response: use
when the frontend needs a response. Use events only for push notifications (backend → frontend) or fire-and-forget.invoke() - Async commands by default: Rust commands should be
to avoid blocking the main thread. Only use sync commands for trivial operations (<1ms).async - Typed IPC wrappers: never call
with raw string command names scattered throughout the frontend. Centralize in a typed module.invoke() - Minimal permissions: start with zero permissions and add only what the app needs. Review permissions on every feature addition.
- Vite over other bundlers: Tauri's tooling is optimized for Vite. Don't use Webpack or Rollup unless forced by a dependency.
Output requirements
Every response must include the applicable sections:
— directory structure with file purposes annotated, if scaffolding a new project or adding a major feature.Project Scaffold
— completeConfiguration Files
snippets (or capability definitions for v2) with security settings annotated.tauri.conf.json
— Rust command definitions paired with their TypeScript invoke wrappers. Include types for both sides.IPC Contract
— build commands, signing setup, and updater configuration if distribution is involved.Build Configuration
(if relevant) — any platform-specific behavior, workarounds, or testing instructions.Platform Notes
Anti-patterns
- Exposing all Tauri APIs: granting
in the allowlist (v1) or"all": true
permissions (v2). This defeats Tauri's security model and gives the WebView access to the full file system, shell, and network."*" - Blocking the main thread: synchronous, long-running Rust commands freeze the entire UI. Always use
commands and spawn blocking work withasync
.tauri::async_runtime::spawn_blocking - Bundling unnecessary permissions: requesting
when the app only needsshell:execute
(open URLs in default browser). Review each permission's actual scope.shell:open - Raw invoke calls: calling
directly in components instead of through typed wrappers. This leads to typo bugs and makes refactoring IPC contracts painful.invoke('my_command', { arg: val }) - History-mode router: using
instead ofRouter
. The app will break on refresh because there's no server to handle the fallback.HashRouter - Duplicated state: keeping the same data in both a SolidJS store and Tauri managed state without a sync mechanism. Pick one source of truth and derive the other.
- Ignoring WebView differences: assuming Chrome DevTools behavior in production. Test on all target platforms — WebKit (macOS), WebView2 (Windows), and WebKitGTK (Linux) have different feature support.
Related skills
— for building backend APIs that a Tauri app might connect tofastapi-patterns
— for analytics dashboards built as Tauri desktop appsbigquery-skill
— for quick utility tasks during developmentmisc-helper
Failure handling
- If the Tauri version is ambiguous, check
for theCargo.toml
crate version andtauri
forpackage.json
version. v1 and v2 have incompatible APIs — confirm before writing code.@tauri-apps/api - If a build fails, check: (1) Rust toolchain installed and up to date, (2) system dependencies for the target platform (e.g.,
on Linux), (3) Vite dev server configuration matcheswebkit2gtk
→tauri.conf.json
.build.devPath - If IPC calls fail silently, verify the command is registered in
and the argument names match exactly between Rust and TypeScript (Tauri uses snake_case → camelCase conversion).generate_handler![] - If the app renders blank on a specific platform, check CSP headers and WebView compatibility. WebKitGTK on Linux is often behind on web API support.