Skip to main content Skills Marketplace Discover and explore AI skills built by the community.
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
Copy promptShow prompt details A direct command skips the review prompt. Inspect the source before running it.
npx skills add https://github.com/bobmatnyc/claude-mpm-skills --skill tauriThe command stays on one line. Scroll horizontally to inspect it before copying.
Prefer a local copy? Download the files currently available to SkillsMP.
Download Zip Downloading... More from this repository LinkedIn automation via the Linked API CLI - fetch profiles, search people and companies, send messages, manage connections, create posts, react, comment, and run Sales Navigator and custom workflows. Use when the user wants to interact with LinkedIn.
Xquik X data automation API - Use REST or MCP for tweet search, user lookup, follower exports, media downloads, monitors, webhooks, giveaway draws, and confirmation-gated X actions.
MCP (Model Context Protocol) - Build AI-native servers with tools, resources, and prompts. TypeScript/Python SDKs for Claude Desktop integration.
Related occupations SOC
Based on SOC occupation classification
name tauri description Advanced Tauri event patterns for bidirectional communication, streaming data, window-to-window messaging, and custom event handling user-invocable false disable-model-invocation true version 1.0.0 category development author Claude MPM Team license MIT progressive_disclosure {"entry_point":{"summary":"Advanced event patterns: bidirectional events, streaming, window messaging, custom payloads, listener management","when_to_use":"When implementing real-time updates, progress tracking, inter-window communication, or streaming data","quick_start":"1. Backend emits with window.emit() 2. Frontend listens with listen() 3. Clean up with unlisten() 4. Use typed payloads"}} context_limit 500 tags ["tauri","events","ipc","streaming","real-time"] requires_tools []
Tauri Advanced Event System
Event Fundamentals
Backend → Frontend Events
Basic event emission :
use tauri::Window;
#[tauri::command]
async fn start_download (
url: String ,
window: Window,
) -> Result <(), String > {
window.emit ("download-started" , url)
.map_err (|e| e.to_string ())?;
window.emit ("download-complete" , "Success" )
.map_err (|e| e.to_string ())
}
Frontend listener :
import { listen, UnlistenFn } from '@tauri-apps/api/event' ;
const unlisten = await listen<string >('download-started' , (event ) => {
console .log ('Download started:' , event.payload );
});
unlisten ();
Structured Event Payloads
Typed Events with Serde
Backend :
use serde::Serialize;
#[derive(Serialize, Clone)]
struct ProgressEvent {
current: ,
total: ,
percentage: ,
message: ,
speed_mbps: < >,
}
(
url: ,
window: Window,
) <(), > {
= (&url). ?;
..total_size {
= ProgressEvent {
current: chunk,
total: total_size,
percentage: (chunk / total_size ) * ,
message: ( , chunk, total_size),
speed_mbps: ( ()),
};
window. ( , progress)
. (|e| e. ())?;
}
(())
}
usize
usize
f64
String
Option
f64
#[tauri::command]
async
fn
download_file
String
->
Result
String
let
total_size
get_file_size
await
for
chunk
in
0
let
progress
as
f64
as
f64
100.0
format!
"Downloading... {}/{}"
Some
calculate_speed
emit
"download-progress"
map_err
to_string
Ok
interface ProgressEvent {
current : number ;
total : number ;
percentage : number ;
message : string ;
speed_mbps ?: number ;
}
const unlisten = await listen<ProgressEvent >('download-progress' , (event ) => {
const { current, total, percentage, message, speed_mbps } = event.payload ;
updateProgressBar (percentage);
updateStatus (message);
if (speed_mbps) {
updateSpeed (speed_mbps);
}
});
Complex Event Payloads #[derive(Serialize, Clone)]
#[serde(tag = "type" , content = "data" )]
enum AppEvent {
UserLoggedIn { user_id: String , username: String },
UserLoggedOut { user_id: String },
DataSynced { items_count: usize , timestamp: String },
ErrorOccurred { code: String , message: String , recoverable: bool },
}
#[tauri::command]
async fn perform_login (
username: String ,
password: String ,
window: Window,
) -> Result <String , String > {
let user = authenticate (&username, &password).await ?;
window.emit ("app-event" , AppEvent::UserLoggedIn {
user_id: user.id.clone (),
username: user.username.clone (),
}).map_err (|e| e.to_string ())?;
Ok (user.id)
}
type AppEvent =
| { type : 'UserLoggedIn' ; data : { user_id : string ; username : string } }
| { type : 'UserLoggedOut' ; data : { user_id : string } }
| { type : 'DataSynced' ; data : { items_count : number ; timestamp : string } }
| { type : 'ErrorOccurred' ; data : { code : string ; message : string ; recoverable : boolean } };
listen<AppEvent >('app-event' , (event ) => {
const appEvent = event.payload ;
switch (appEvent.type ) {
case 'UserLoggedIn' :
handleLogin (appEvent.data .user_id , appEvent.data .username );
break ;
case 'UserLoggedOut' :
handleLogout (appEvent.data .user_id );
break ;
case 'DataSynced' :
showSyncSuccess (appEvent.data .items_count );
break ;
case 'ErrorOccurred' :
handleError (appEvent.data );
break ;
}
});
Streaming Data Patterns
Real-Time Data Stream #[tauri::command]
async fn stream_sensor_data (
sensor_id: String ,
window: Window,
) -> Result <(), String > {
let mut interval = tokio::time::interval (Duration::from_millis (100 ));
for _ in 0 ..100 {
interval.tick ().await ;
let reading = read_sensor (&sensor_id).await ?;
window.emit ("sensor-reading" , reading)
.map_err (|e| e.to_string ())?;
}
window.emit ("sensor-stream-ended" , sensor_id)
.map_err (|e| e.to_string ())
}
import { useEffect, useState } from 'react' ;
import { listen } from '@tauri-apps/api/event' ;
interface SensorReading {
value : number ;
timestamp : number ;
unit : string ;
}
function SensorMonitor ( ) {
const [readings, setReadings] = useState<SensorReading []>([]);
useEffect (() => {
let unlisten : UnlistenFn | undefined ;
listen<SensorReading >('sensor-reading' , (event ) => {
setReadings (prev => [...prev.slice (-99 ), event.payload ]);
}).then (fn => unlisten = fn);
return () => unlisten?.();
}, []);
return (
<div >
{readings.map((r, i) => (
<div key ={i} > {r.value} {r.unit}</div >
))}
</div >
);
}
Buffered Streaming #[tauri::command]
async fn stream_logs (
log_file: String ,
window: Window,
) -> Result <(), String > {
use tokio::io::{AsyncBufReadExt, BufReader};
use tokio::fs::File;
let file = File::open (log_file).await
.map_err (|e| e.to_string ())?;
let reader = BufReader::new (file);
let mut lines = reader.lines ();
let mut buffer = Vec ::new ();
while let Some (line) = lines.next_line ().await
.map_err (|e| e.to_string ())? {
buffer.push (line);
if buffer.len () >= 10 {
window.emit ("log-batch" , buffer.clone ())
.map_err (|e| e.to_string ())?;
buffer.clear ();
}
}
if !buffer.is_empty () {
window.emit ("log-batch" , buffer)
.map_err (|e| e.to_string ())?;
}
Ok (())
}
Multi-Window Communication
Broadcasting to All Windows use tauri::{AppHandle, Manager};
#[tauri::command]
async fn broadcast_message (
message: String ,
app: AppHandle,
) -> Result <(), String > {
app.emit_all ("broadcast" , message)
.map_err (|e| e.to_string ())
}
Targeted Window Messaging #[tauri::command]
async fn send_to_window (
target_window: String ,
message: String ,
app: AppHandle,
) -> Result <(), String > {
if let Some (window) = app.get_window (&target_window) {
window.emit ("private-message" , message)
.map_err (|e| e.to_string ())?;
Ok (())
} else {
Err (format! ("Window '{}' not found" , target_window))
}
}
Window-to-Window via Backend import { invoke } from '@tauri-apps/api/core' ;
async function sendToSettings (data : any ) {
await invoke ('relay_to_settings' , { data });
}
#[tauri::command]
async fn relay_to_settings (
data: serde_json::Value,
app: AppHandle,
) -> Result <(), String > {
if let Some (settings_window) = app.get_window ("settings" ) {
settings_window.emit ("data-update" , data)
.map_err (|e| e.to_string ())?;
}
Ok (())
}
Window B (receiver - settings) :
import { listen } from '@tauri-apps/api/event' ;
useEffect (() => {
let unlisten : UnlistenFn | undefined ;
listen ('data-update' , (event ) => {
console .log ('Received from main window:' , event.payload );
updateSettings (event.payload );
}).then (fn => unlisten = fn);
return () => unlisten?.();
}, []);
Frontend → Backend Events
Custom Frontend Events import { emit } from '@tauri-apps/api/event' ;
await emit ('user-action' , {
action : 'button-click' ,
button_id : 'save-button' ,
timestamp : Date .now ()
});
use tauri::{Manager, Listener};
fn main () {
tauri::Builder::default ()
.setup (|app| {
let app_handle = app.handle ();
app_handle.listen_global ("user-action" , move |event| {
if let Some (payload) = event.payload () {
println! ("User action: {}" , payload);
}
});
Ok (())
})
.run (tauri::generate_context!())
.expect ("error while running tauri application" );
}
Advanced Listener Management
React Hook for Events import { useEffect, useState } from 'react' ;
import { listen, UnlistenFn } from '@tauri-apps/api/event' ;
function useEvent<T>(eventName : string ): T | null {
const [payload, setPayload] = useState<T | null >(null );
useEffect (() => {
let unlisten : UnlistenFn | undefined ;
listen<T>(eventName, (event ) => {
setPayload (event.payload );
}).then (fn => unlisten = fn);
return () => unlisten?.();
}, [eventName]);
return payload;
}
function ProgressDisplay ( ) {
const progress = useEvent<ProgressEvent >('download-progress' );
if (!progress) return null ;
return (
<div >
Progress: {progress.percentage.toFixed(2)}%
</div >
);
}
Event Queue Pattern import { listen } from '@tauri-apps/api/event' ;
class EventQueue <T> {
private queue : T[] = [];
private unlisten ?: UnlistenFn ;
async start (eventName : string ) {
this .unlisten = await listen<T>(eventName, (event ) => {
this .queue .push (event.payload );
});
}
dequeue (): T | undefined {
return this .queue .shift ();
}
clear ( ) {
this .queue = [];
}
stop ( ) {
this .unlisten ?.();
}
get length () {
return this .queue .length ;
}
}
const progressQueue = new EventQueue <ProgressEvent >();
await progressQueue.start ('download-progress' );
setInterval (() => {
while (progressQueue.length > 0 ) {
const event = progressQueue.dequeue ();
processProgress (event);
}
}, 100 );
One-Time Events import { once } from '@tauri-apps/api/event' ;
await once<string >('initialization-complete' , (event ) => {
console .log ('App initialized:' , event.payload );
startApp ();
});
Error Handling in Events
Safe Event Emission async fn emit_safe (window: &Window, event: &str , payload: impl Serialize ) -> Result <(), String > {
window.emit (event, payload)
.map_err (|e| {
eprintln! ("Failed to emit event '{}': {}" , event, e);
e.to_string ()
})
}
#[tauri::command]
async fn process_with_events (
window: Window,
) -> Result <(), String > {
emit_safe (&window, "processing-started" , "Starting..." )
.await ?;
emit_safe (&window, "processing-complete" , "Done!" )
.await ?;
Ok (())
}
Performance Considerations
Throttling Events use std::time::{Duration, Instant};
#[tauri::command]
async fn high_frequency_updates (
window: Window,
) -> Result <(), String > {
let mut last_emit = Instant::now ();
let throttle_duration = Duration::from_millis (100 );
for i in 0 ..10000 {
let value = compute_value (i);
if last_emit.elapsed () >= throttle_duration {
window.emit ("update" , value)
.map_err (|e| e.to_string ())?;
last_emit = Instant::now ();
}
}
Ok (())
}
Batching Events #[tauri::command]
async fn batch_updates (
window: Window,
) -> Result <(), String > {
let mut batch = Vec ::new ();
for item in process_items () {
batch.push (item);
if batch.len () >= 50 {
window.emit ("batch-update" , batch.clone ())
.map_err (|e| e.to_string ())?;
batch.clear ();
}
}
if !batch.is_empty () {
window.emit ("batch-update" , batch)
.map_err (|e| e.to_string ())?;
}
Ok (())
}
Best Practices
Always clean up listeners - Use unlisten() to prevent memory leaks
Type event payloads - Define interfaces for type safety
Use structured events - Tagged unions for multiple event types
Throttle high-frequency events - Prevent overwhelming frontend
Batch when possible - Reduce serialization overhead
Handle errors gracefully - Log failed emissions, don't crash
Use once() for one-time events - Initialization, completion signals
Namespace event names - Use prefixes like "download:", "user:", "system:"
Common Pitfalls ❌ Forgetting to unlisten :
function Component ( ) {
listen ('my-event' , handler);
}
function Component ( ) {
useEffect (() => {
let unlisten : UnlistenFn | undefined ;
listen ('my-event' , handler).then (fn => unlisten = fn);
return () => unlisten?.();
}, []);
}
❌ Not handling serialization errors :
#[derive(Clone)]
struct Event { }
window.emit ("event" , Event {});
#[derive(Serialize, Clone)]
struct Event { }
❌ Emitting too frequently :
for i in 0 ..10000 {
window.emit ("update" , i);
}
Summary
Events are async - Backend → Frontend communication
Always type payloads - Use serde::Serialize + TypeScript interfaces
Clean up listeners - Call unlisten() in cleanup
Throttle/batch - High-frequency events need rate limiting
Use structured payloads - Tagged unions for multiple event types
Window targeting - emit() for specific, emit_all() for broadcast
Frontend events - Use emit() from frontend, listen in backend setup