0
# Event System
1
2
Real-time bidirectional communication system between frontend and backend with support for custom events, built-in system events, and flexible targeting options.
3
4
## Capabilities
5
6
### Event Listening
7
8
Listen for events from the backend or other parts of the application.
9
10
```typescript { .api }
11
/**
12
* Listen for an event and call handler each time it occurs
13
* @param event - Event name to listen for
14
* @param handler - Function to call when event occurs
15
* @param options - Optional targeting and configuration
16
* @returns Promise resolving to function that removes the listener
17
*/
18
function listen<T>(
19
event: EventName,
20
handler: EventCallback<T>,
21
options?: Options
22
): Promise<UnlistenFn>;
23
24
/**
25
* Listen for an event and call handler only once
26
* @param event - Event name to listen for
27
* @param handler - Function to call when event occurs
28
* @param options - Optional targeting and configuration
29
* @returns Promise resolving to function that removes the listener
30
*/
31
function once<T>(
32
event: EventName,
33
handler: EventCallback<T>,
34
options?: Options
35
): Promise<UnlistenFn>;
36
37
type EventCallback<T> = (event: Event<T>) => void;
38
type UnlistenFn = () => void;
39
type EventName = string;
40
41
interface Options {
42
target?: EventTarget;
43
}
44
```
45
46
### Event Emission
47
48
Send events to other parts of the application or the backend.
49
50
```typescript { .api }
51
/**
52
* Emit an event to all listeners
53
* @param event - Event name to emit
54
* @param payload - Optional data to send with event
55
*/
56
function emit<T>(event: string, payload?: T): Promise<void>;
57
58
/**
59
* Emit an event to a specific target
60
* @param target - Target to send event to
61
* @param event - Event name to emit
62
* @param payload - Optional data to send with event
63
*/
64
function emitTo<T>(
65
target: EventTarget | string,
66
event: string,
67
payload?: T
68
): Promise<void>;
69
```
70
71
### Event Objects
72
73
Structure of events received by handlers.
74
75
```typescript { .api }
76
/**
77
* Event object passed to event handlers
78
*/
79
interface Event<T> {
80
/** Event name */
81
event: string;
82
/** Unique event ID */
83
id: number;
84
/** Event payload data */
85
payload: T;
86
}
87
```
88
89
### Event Targeting
90
91
Control which components receive events.
92
93
```typescript { .api }
94
/**
95
* Event targeting options
96
*/
97
type EventTarget =
98
| { Any: null }
99
| { AnyLabel: { label: string } }
100
| { App: null }
101
| { Window: { label: string } }
102
| { Webview: { label: string } }
103
| { WebviewWindow: { label: string } };
104
```
105
106
### Built-in System Events
107
108
Predefined events automatically emitted by Tauri for system state changes.
109
110
```typescript { .api }
111
/**
112
* Built-in Tauri events
113
*/
114
enum TauriEvent {
115
// Window events
116
WINDOW_RESIZED = 'tauri://resize',
117
WINDOW_MOVED = 'tauri://move',
118
WINDOW_CLOSE_REQUESTED = 'tauri://close-requested',
119
WINDOW_DESTROYED = 'tauri://destroyed',
120
WINDOW_FOCUS = 'tauri://focus',
121
WINDOW_BLUR = 'tauri://blur',
122
WINDOW_SCALE_FACTOR_CHANGED = 'tauri://scale-change',
123
WINDOW_THEME_CHANGED = 'tauri://theme-changed',
124
WINDOW_FILE_DROP = 'tauri://file-drop',
125
WINDOW_FILE_DROP_HOVER = 'tauri://file-drop-hover',
126
WINDOW_FILE_DROP_CANCELLED = 'tauri://file-drop-cancelled',
127
128
// Webview events
129
WEBVIEW_CREATED = 'tauri://webview-created',
130
131
// Menu events
132
MENU = 'tauri://menu',
133
134
// Tray events
135
TRAY_CLICK = 'tauri://tray-click',
136
TRAY_DOUBLE_CLICK = 'tauri://tray-double-click',
137
TRAY_RIGHT_CLICK = 'tauri://tray-right-click',
138
TRAY_ENTER = 'tauri://tray-enter',
139
TRAY_LEAVE = 'tauri://tray-leave',
140
TRAY_MOVE = 'tauri://tray-move'
141
}
142
```
143
144
### File Drop Events
145
146
Events for handling file drag-and-drop operations.
147
148
```typescript { .api }
149
/**
150
* File drop event types
151
*/
152
type DragDropEvent =
153
| { type: 'drop'; paths: string[]; position: PhysicalPosition }
154
| { type: 'hover'; paths: string[]; position: PhysicalPosition }
155
| { type: 'cancel' };
156
```
157
158
## Usage Examples
159
160
### Basic Event Listening
161
162
```typescript
163
import { listen, once } from '@tauri-apps/api/event';
164
165
// Listen for custom events from backend
166
const unlisten = await listen<string>('backend-message', (event) => {
167
console.log('Received message:', event.payload);
168
console.log('Event ID:', event.id);
169
});
170
171
// Listen for event only once
172
await once<{ status: string }>('initialization-complete', (event) => {
173
console.log('App initialized with status:', event.payload.status);
174
});
175
176
// Remove listener when done
177
unlisten();
178
```
179
180
### System Event Handling
181
182
```typescript
183
import { listen, TauriEvent } from '@tauri-apps/api/event';
184
185
// Handle window resize
186
await listen(TauriEvent.WINDOW_RESIZED, (event) => {
187
const { width, height } = event.payload;
188
console.log(`Window resized to ${width}x${height}`);
189
// Adjust UI layout
190
});
191
192
// Handle window focus/blur
193
await listen(TauriEvent.WINDOW_FOCUS, () => {
194
document.body.classList.add('focused');
195
});
196
197
await listen(TauriEvent.WINDOW_BLUR, () => {
198
document.body.classList.remove('focused');
199
});
200
201
// Handle theme changes
202
await listen(TauriEvent.WINDOW_THEME_CHANGED, (event) => {
203
const theme = event.payload.theme; // 'light' | 'dark'
204
document.documentElement.setAttribute('data-theme', theme);
205
});
206
```
207
208
### File Drop Handling
209
210
```typescript
211
import { listen, TauriEvent } from '@tauri-apps/api/event';
212
213
// Handle file drops
214
await listen<DragDropEvent>(TauriEvent.WINDOW_FILE_DROP, (event) => {
215
if (event.payload.type === 'drop') {
216
const files = event.payload.paths;
217
console.log('Files dropped:', files);
218
processDroppedFiles(files);
219
}
220
});
221
222
// Handle drag hover
223
await listen<DragDropEvent>(TauriEvent.WINDOW_FILE_DROP_HOVER, (event) => {
224
if (event.payload.type === 'hover') {
225
document.body.classList.add('drag-over');
226
showDropZone();
227
}
228
});
229
230
// Handle drag cancel
231
await listen<DragDropEvent>(TauriEvent.WINDOW_FILE_DROP_CANCELLED, () => {
232
document.body.classList.remove('drag-over');
233
hideDropZone();
234
});
235
236
function processDroppedFiles(files: string[]) {
237
files.forEach(async (file) => {
238
if (file.endsWith('.json')) {
239
// Process JSON files
240
const content = await readTextFile(file);
241
console.log('JSON content:', JSON.parse(content));
242
} else if (file.match(/\.(jpg|png|gif)$/i)) {
243
// Process image files
244
const image = await Image.fromPath(file);
245
displayImage(image);
246
}
247
});
248
}
249
```
250
251
### Custom Event Communication
252
253
```typescript
254
import { emit, emitTo, listen } from '@tauri-apps/api/event';
255
256
// Frontend to backend communication
257
await emit('user-action', {
258
type: 'button-click',
259
buttonId: 'save-btn'
260
});
261
262
// Send data to backend for processing
263
await emit('process-data', {
264
data: [1, 2, 3, 4, 5],
265
algorithm: 'quicksort'
266
});
267
268
// Listen for backend responses
269
await listen<{ result: number[] }>('data-processed', (event) => {
270
console.log('Processed data:', event.payload.result);
271
});
272
273
// Inter-window communication
274
await emitTo('settings-window', 'config-updated', {
275
theme: 'dark',
276
language: 'en'
277
});
278
```
279
280
### Targeted Event Emission
281
282
```typescript
283
import { emitTo } from '@tauri-apps/api/event';
284
285
// Send to specific window
286
await emitTo(
287
{ Window: { label: 'main-window' } },
288
'refresh-data',
289
{ timestamp: Date.now() }
290
);
291
292
// Send to specific webview
293
await emitTo(
294
{ Webview: { label: 'content-view' } },
295
'scroll-to-top',
296
null
297
);
298
299
// Send to all windows with specific label pattern
300
await emitTo(
301
{ AnyLabel: { label: 'editor-*' } },
302
'save-all',
303
{ force: true }
304
);
305
306
// Send to entire application
307
await emitTo(
308
{ App: null },
309
'global-setting-changed',
310
{ setting: 'theme', value: 'dark' }
311
);
312
```
313
314
### Event-Driven Architecture
315
316
```typescript
317
import { listen, emit } from '@tauri-apps/api/event';
318
319
class EventBus {
320
private listeners = new Map<string, Function[]>();
321
322
async subscribe<T>(event: string, callback: (payload: T) => void) {
323
const unlistenFn = await listen<T>(event, (e) => callback(e.payload));
324
325
if (!this.listeners.has(event)) {
326
this.listeners.set(event, []);
327
}
328
this.listeners.get(event)!.push(unlistenFn);
329
330
return unlistenFn;
331
}
332
333
async publish<T>(event: string, payload: T) {
334
await emit(event, payload);
335
}
336
337
cleanup() {
338
this.listeners.forEach(unlisteners => {
339
unlisteners.forEach(unlisten => unlisten());
340
});
341
this.listeners.clear();
342
}
343
}
344
345
// Usage
346
const eventBus = new EventBus();
347
348
// Subscribe to events
349
await eventBus.subscribe<string>('user-login', (username) => {
350
console.log(`User ${username} logged in`);
351
updateUI();
352
});
353
354
await eventBus.subscribe<{ error: string }>('api-error', (data) => {
355
showErrorNotification(data.error);
356
});
357
358
// Publish events
359
await eventBus.publish('user-login', 'john_doe');
360
await eventBus.publish('api-error', { error: 'Network timeout' });
361
```
362
363
### Menu Event Integration
364
365
```typescript
366
import { listen } from '@tauri-apps/api/event';
367
368
// Handle menu item clicks
369
await listen<{ menuItemId: string }>('tauri://menu', (event) => {
370
const menuId = event.payload.menuItemId;
371
372
switch (menuId) {
373
case 'file-new':
374
createNewFile();
375
break;
376
case 'file-open':
377
openFileDialog();
378
break;
379
case 'edit-copy':
380
copyToClipboard();
381
break;
382
case 'view-toggle-sidebar':
383
toggleSidebar();
384
break;
385
default:
386
console.log('Unknown menu item:', menuId);
387
}
388
});
389
```
390
391
### Tray Icon Events
392
393
```typescript
394
import { listen } from '@tauri-apps/api/event';
395
396
// Handle tray icon interactions
397
await listen('tauri://tray-click', (event) => {
398
console.log('Tray clicked:', event.payload);
399
toggleMainWindow();
400
});
401
402
await listen('tauri://tray-double-click', () => {
403
showMainWindow();
404
});
405
406
await listen('tauri://tray-right-click', () => {
407
// Context menu automatically shown
408
console.log('Tray right-clicked');
409
});
410
```
411
412
### Error Handling and Cleanup
413
414
```typescript
415
import { listen } from '@tauri-apps/api/event';
416
417
class ComponentWithEvents {
418
private unlisteners: (() => void)[] = [];
419
420
async initialize() {
421
// Register multiple event listeners
422
const unlisten1 = await listen('event1', this.handleEvent1.bind(this));
423
const unlisten2 = await listen('event2', this.handleEvent2.bind(this));
424
const unlisten3 = await listen('event3', this.handleEvent3.bind(this));
425
426
this.unlisteners.push(unlisten1, unlisten2, unlisten3);
427
}
428
429
private handleEvent1(event: Event<any>) {
430
try {
431
// Handle event
432
} catch (error) {
433
console.error('Error handling event1:', error);
434
}
435
}
436
437
private handleEvent2(event: Event<any>) {
438
// Handle event
439
}
440
441
private handleEvent3(event: Event<any>) {
442
// Handle event
443
}
444
445
cleanup() {
446
// Remove all listeners when component is destroyed
447
this.unlisteners.forEach(unlisten => unlisten());
448
this.unlisteners = [];
449
}
450
}
451
452
// Usage
453
const component = new ComponentWithEvents();
454
await component.initialize();
455
456
// Later, when component is no longer needed
457
component.cleanup();
458
```
459
460
### Performance Considerations
461
462
```typescript
463
import { listen, emit } from '@tauri-apps/api/event';
464
465
// Debounce high-frequency events
466
function debounce<T extends (...args: any[]) => void>(
467
func: T,
468
wait: number
469
): T {
470
let timeout: NodeJS.Timeout;
471
return ((...args: any[]) => {
472
clearTimeout(timeout);
473
timeout = setTimeout(() => func.apply(this, args), wait);
474
}) as T;
475
}
476
477
// Handle resize events efficiently
478
const debouncedResize = debounce((size: { width: number; height: number }) => {
479
console.log('Window resized to:', size);
480
// Expensive layout recalculation
481
}, 250);
482
483
await listen(TauriEvent.WINDOW_RESIZED, (event) => {
484
debouncedResize(event.payload);
485
});
486
487
// Batch multiple events
488
let eventQueue: any[] = [];
489
const processQueue = () => {
490
if (eventQueue.length > 0) {
491
console.log('Processing', eventQueue.length, 'events');
492
// Process all queued events at once
493
eventQueue = [];
494
}
495
};
496
497
await listen('high-frequency-event', (event) => {
498
eventQueue.push(event.payload);
499
});
500
501
// Process queue periodically
502
setInterval(processQueue, 100);
503
```