Zubridge API Reference
April 29, 2025 ยท View on GitHub
This document provides a comprehensive reference of both the Tauri plugin API and the frontend JavaScript API.
Tauri Plugin API (tauri-plugin-zubridge)
Core Types
StateManager Trait
pub trait StateManager: Send + Sync + 'static {
fn get_state(&self) -> serde_json::Value;
fn process_action(&self, action: &ZubridgeAction) -> Result<(), String>;
}
The central trait that your state manager must implement:
get_state(): Returns the current state as a JSON value that will be sent to the frontendprocess_action(): Processes an action from the frontend and updates the state accordingly
ZubridgeAction Struct
#[derive(Debug, serde::Deserialize, Clone)]
pub struct ZubridgeAction {
#[serde(rename = "type")]
pub action_type: String,
pub payload: Option<serde_json::Value>,
}
Represents an action dispatched from the frontend:
action_type: String identifying the action (e.g., "INCREMENT")payload: Optional JSON data associated with the action
ZubridgePlugin Struct
pub struct ZubridgePlugin<M: StateManager> {
state_manager: M,
}
The main plugin struct that wraps your state manager.
Functions
ZubridgePlugin::new()
pub fn new<M: StateManager>(state_manager: M) -> TauriPlugin<R>
Creates a new Zubridge plugin instance:
- Parameters:
state_manager: An instance implementing theStateManagertrait
- Returns: A Tauri plugin that can be registered with your application
Usage Example
use tauri_plugin_zubridge::{StateManager, ZubridgePlugin, ZubridgeAction};
use std::sync::Mutex;
use serde::{Serialize, Deserialize};
// Define your state
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct AppState {
counter: i32,
}
impl Default for AppState {
fn default() -> Self {
Self { counter: 0 }
}
}
// Implement the StateManager trait
struct AppStateManager {
state: Mutex<AppState>,
}
impl StateManager for AppStateManager {
fn get_state(&self) -> serde_json::Value {
let state = self.state.lock().unwrap();
serde_json::to_value(&*state).unwrap()
}
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)),
}
}
}
// Create and register the plugin
pub fn zubridge<R: Runtime>() -> TauriPlugin<R> {
let state_manager = AppStateManager {
state: Mutex::new(AppState::default()),
};
ZubridgePlugin::new(state_manager)
}
Frontend API (@zubridge/tauri)
Core Functions
initializeBridge()
function initializeBridge(options: {
invoke: (cmd: string, args?: any) => Promise<any>;
listen: (event: string, callback: (event: any) => void) => Promise<() => void>;
}): void;
Initializes the Zubridge bridge with the necessary Tauri functions:
- Parameters:
options.invoke: Function to invoke Tauri commandsoptions.listen: Function to listen for Tauri events
- Returns: void
Example:
import { initializeBridge } from '@zubridge/tauri';
import { invoke } from '@tauri-apps/api/core'; // For Tauri v2
import { listen } from '@tauri-apps/api/event';
initializeBridge({ invoke, listen });
Hooks
useZubridgeStore()
function useZubridgeStore<T = any>(selector?: (state: any) => T): T;
A hook to access the synchronized state:
- Parameters:
selector: Optional function to select a slice of the state
- Returns: The selected state or the entire state if no selector is provided
Example:
import { useZubridgeStore } from '@zubridge/tauri';
function Counter() {
const counter = useZubridgeStore((state) => state.counter);
// Access internal bridge status
const status = useZubridgeStore((state) => state.__bridge_status);
return <div>Counter: {counter}</div>;
}
useZubridgeDispatch()
function useZubridgeDispatch(): (action: { type: string; payload?: any }) => Promise<void>;
A hook to get the dispatch function for sending actions to the backend:
- Returns: A function that dispatches actions to the backend
Example:
import { useZubridgeDispatch } from '@zubridge/tauri';
function Counter() {
const dispatch = useZubridgeDispatch();
return <button onClick={() => dispatch({ type: 'INCREMENT' })}>Increment</button>;
}
Store Methods
The Zubridge store also exposes these methods for non-hook usage:
getState()
function getState(): any;
Returns the current state:
- Returns: The entire current state object
Example:
import { useZubridgeStore } from '@zubridge/tauri';
const currentState = useZubridgeStore.getState();
console.log(currentState.counter);
subscribe()
function subscribe(listener: (state: any, prevState: any) => void): () => void;
Subscribes to state changes:
- Parameters:
listener: Function called whenever the state changes
- Returns: Unsubscribe function
Example:
import { useZubridgeStore } from '@zubridge/tauri';
const unsubscribe = useZubridgeStore.subscribe((state, prevState) => console.log('State changed:', state, prevState));
// Later, to unsubscribe:
unsubscribe();
Internal State Properties
The store contains special internal properties prefixed with __bridge_:
__bridge_status: String indicating the bridge status ('initializing', 'ready', or 'error')__bridge_error: Any error that occurred during bridge operations
Example:
import { useZubridgeStore } from '@zubridge/tauri';
function App() {
const status = useZubridgeStore(state => state.__bridge_status);
const error = useZubridgeStore(state => state.__bridge_error);
if (status === 'initializing') {
return <div>Loading...</div>;
}
if (status === 'error') {
return <div>Error: {String(error)}</div>;
}
return <div>App loaded!</div>;
}
Action Pattern
Actions follow a standard pattern inspired by Redux:
interface ZubridgeAction {
type: string;
payload?: any;
}
type: String identifier for the action (e.g., 'INCREMENT', 'ADD_TODO')payload: Optional data associated with the action
Examples:
// Simple action without payload
dispatch({ type: 'INCREMENT' });
// Action with payload
dispatch({
type: 'ADD_TODO',
payload: { text: 'Buy milk', completed: false },
});
// Action with primitive payload
dispatch({
type: 'SET_COUNTER',
payload: 5,
});
Error Handling
Errors from the backend are captured and stored in the __bridge_error property:
function ErrorBoundary() {
const error = useZubridgeStore(state => state.__bridge_error);
if (error) {
return <div className="error">Error: {String(error)}</div>;
}
return null;
}
Vanilla JavaScript Usage
While the library uses hook naming conventions, it can be used with any JavaScript framework or vanilla JS:
import { useZubridgeStore, useZubridgeDispatch } from '@zubridge/tauri';
// Access state directly
const counter = useZubridgeStore.getState().counter;
// Subscribe to changes
const unsubscribe = useZubridgeStore.subscribe((state) => {
console.log('Counter:', state.counter);
document.getElementById('counter').textContent = state.counter;
});
// Dispatch actions
const dispatch = useZubridgeDispatch();
document.getElementById('increment').addEventListener('click', () => {
dispatch({ type: 'INCREMENT' });
});
// Clean up
window.addEventListener('beforeunload', () => {
unsubscribe();
});