| Crates.io | tauri-plugin-zubridge |
| lib.rs | tauri-plugin-zubridge |
| version | 0.1.0 |
| created_at | 2025-05-13 10:46:19.819796+00 |
| updated_at | 2025-05-13 10:46:19.819796+00 |
| description | A Tauri plugin for state management between frontend and backend |
| homepage | https://github.com/goosewobbler/zubridge/tree/main/packages/tauri-plugin-zubridge |
| repository | https://github.com/goosewobbler/zubridge |
| max_upload_size | |
| id | 1671706 |
| size | 18,164 |
Cross-platform state without boundaries: The official Tauri plugin for 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.
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.
@zubridge/tauriuseZubridgeStore and dispatch actions with useZubridgeDispatch
[dependencies]
tauri-plugin-zubridge = "0.1.0"
serde = { version = "1.0", features = ["derive"] }
npm install @zubridge/tauri @tauri-apps/api
Or use your dependency manager of choice, e.g. pnpm, yarn.
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");
}
// 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>
);
}
For more detailed documentation, see:
A complete example application demonstrating the use of tauri-plugin-zubridge with a simple counter state:
MIT