1 unstable release
new 0.1.0 | May 13, 2025 |
---|
#759 in GUI
19KB
236 lines

Zubridge Tauri Plugin
Cross-platform state without boundaries: The official Tauri plugin for Zubridge
Why Zubridge?
tldr: I want to seamlessly interact with my Rust backend state using Zustand-inspired hooks.
Managing state between a Tauri backend and frontend requires implementing event listeners and command handlers. The tauri-plugin-zubridge
plugin eliminates this boilerplate by providing a standardized approach for state management that works with the @zubridge/tauri
frontend library.
How It Works
Zubridge creates a bridge between your Rust backend state and your frontend JavaScript. Your Rust backend holds the source of truth, while the frontend uses hooks to access and update this state.
- Backend: Register the plugin with your app state
- Backend: Use the StateManager trait to handle state changes
- Frontend: Initialize the bridge with
@zubridge/tauri
- Frontend: Access state with
useZubridgeStore
and dispatch actions withuseZubridgeDispatch

Features
- Simple State Management: Manages synchronization between Rust backend and JavaScript frontend
- Standard Interface: Provides a consistent pattern for dispatching actions and receiving updates
- Type Safety: Strong typing for both Rust and TypeScript sides
- Multi-Window Support: Automatically broadcasts state changes to all windows
- Minimal Boilerplate: Reduces the amount of code needed for state management
- Flexible Implementation: Use with any frontend framework (React, Vue, Svelte, etc.)
Installation
Cargo.toml
[dependencies]
tauri-plugin-zubridge = "0.1.0"
serde = { version = "1.0", features = ["derive"] }
Frontend
npm install @zubridge/tauri @tauri-apps/api
Or use your dependency manager of choice, e.g. pnpm
, yarn
.
Quick Start
Rust Backend
use serde::{Deserialize, Serialize};
use tauri::{plugin::TauriPlugin, AppHandle, Runtime};
use tauri_plugin_zubridge::{StateManager, ZubridgePlugin};
// 1. Define your state
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct AppState {
counter: i32,
}
impl Default for AppState {
fn default() -> Self {
Self { counter: 0 }
}
}
// 2. Implement StateManager for your state
struct AppStateManager {
state: std::sync::Mutex<AppState>,
}
impl StateManager for AppStateManager {
// Get the current state
fn get_state(&self) -> serde_json::Value {
let state = self.state.lock().unwrap();
serde_json::to_value(&*state).unwrap()
}
// Process actions dispatched from the frontend
fn process_action(&self, action: &ZubridgeAction) -> Result<(), String> {
let mut state = self.state.lock().unwrap();
match action.action_type.as_str() {
"INCREMENT" => {
state.counter += 1;
Ok(())
},
"DECREMENT" => {
state.counter -= 1;
Ok(())
},
_ => Err(format!("Unknown action: {}", action.action_type)),
}
}
}
// 3. Create a plugin function
pub fn zubridge<R: Runtime>() -> TauriPlugin<R> {
let state_manager = AppStateManager {
state: std::sync::Mutex::new(AppState::default()),
};
ZubridgePlugin::new(state_manager)
}
// 4. Register the plugin in your main.rs
fn main() {
tauri::Builder::default()
.plugin(zubridge())
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
Frontend
// main.tsx
import { initializeBridge } from '@zubridge/tauri';
import { invoke } from '@tauri-apps/api/core';
import { listen } from '@tauri-apps/api/event';
// Initialize the bridge
initializeBridge({ invoke, listen });
// Component.tsx
import { useZubridgeStore, useZubridgeDispatch } from '@zubridge/tauri';
function Counter() {
// Get state from the bridge
const counter = useZubridgeStore((state) => state.counter);
// Get dispatch function
const dispatch = useZubridgeDispatch();
return (
<div>
<h1>Counter: {counter}</h1>
<button onClick={() => dispatch({ type: 'INCREMENT' })}>+</button>
<button onClick={() => dispatch({ type: 'DECREMENT' })}>-</button>
</div>
);
}
Documentation
For more detailed documentation, see:
Example Application
A complete example application demonstrating the use of tauri-plugin-zubridge
with a simple counter state:
License
MIT
Dependencies
~18–54MB
~866K SLoC