0
# Event Management
1
2
Uppy provides a comprehensive event system for handling all aspects of the file upload lifecycle. Events can be used for monitoring progress, implementing custom logic, and integrating with external systems.
3
4
## EventManager Class
5
6
Utility class for managing Uppy event subscriptions with automatic cleanup capabilities.
7
8
### Constructor
9
10
```typescript { .api }
11
constructor(uppy: Uppy<M, B>)
12
```
13
14
### Event Subscription
15
16
```typescript { .api }
17
on<K extends keyof UppyEventMap<M, B>>(
18
event: K,
19
fn: UppyEventMap<M, B>[K]
20
): Uppy<M, B>;
21
```
22
23
Subscribes to an event and tracks the subscription for later cleanup.
24
25
### Cleanup
26
27
```typescript { .api }
28
remove(): void;
29
```
30
31
Removes all event listeners registered through this EventManager instance.
32
33
### File-Specific Event Helpers
34
35
```typescript { .api }
36
onFilePause(fileID: UppyFile<M, B>['id'], cb: (isPaused: boolean) => void): void;
37
onFileRemove(fileID: UppyFile<M, B>['id'], cb: (isPaused: UppyFile<M, B>['id']) => void): void;
38
onPause(fileID: UppyFile<M, B>['id'], cb: (isPaused: boolean) => void): void;
39
onRetry(fileID: UppyFile<M, B>['id'], cb: () => void): void;
40
onRetryAll(fileID: UppyFile<M, B>['id'], cb: () => void): void;
41
onPauseAll(fileID: UppyFile<M, B>['id'], cb: () => void): void;
42
onCancelAll(fileID: UppyFile<M, B>['id'], eventHandler: UppyEventMap<M, B>['cancel-all']): void;
43
onResumeAll(fileID: UppyFile<M, B>['id'], cb: () => void): void;
44
```
45
46
These methods listen for global events but only trigger callbacks when they affect the specified file.
47
48
## Core Uppy Events
49
50
### File Events
51
52
```typescript { .api }
53
interface UppyEventMap<M extends Meta, B extends Body> {
54
'file-added': (file: UppyFile<M, B>) => void;
55
'file-removed': (file: UppyFile<M, B>) => void;
56
'files-added': (files: UppyFile<M, B>[]) => void;
57
}
58
```
59
60
**file-added**: Fired when a single file is added to Uppy
61
**file-removed**: Fired when a file is removed from Uppy
62
**files-added**: Fired when multiple files are added at once
63
64
### Upload Control Events
65
66
```typescript { .api }
67
interface UppyEventMap<M extends Meta, B extends Body> {
68
'upload': (uploadID: string, files: UppyFile<M, B>[]) => void;
69
'upload-start': (files: UppyFile<M, B>[]) => void;
70
'cancel-all': () => void;
71
'pause-all': () => void;
72
'resume-all': () => void;
73
'retry-all': (files: UppyFile<M, B>[]) => void;
74
}
75
```
76
77
**upload**: Fired when upload process begins
78
**upload-start**: Alternative event for upload start
79
**cancel-all**: Fired when all uploads are cancelled
80
**pause-all**: Fired when all uploads are paused
81
**resume-all**: Fired when all uploads are resumed
82
**retry-all**: Fired when retrying all failed uploads
83
84
### Upload Progress Events
85
86
```typescript { .api }
87
interface UppyEventMap<M extends Meta, B extends Body> {
88
'upload-progress': (
89
file: UppyFile<M, B> | undefined,
90
progress: FileProgressStarted
91
) => void;
92
'progress': (progress: number) => void;
93
'upload-success': (
94
file: UppyFile<M, B> | undefined,
95
response: NonNullable<UppyFile<M, B>['response']>
96
) => void;
97
'upload-error': (
98
file: UppyFile<M, B> | undefined,
99
error: { name: string; message: string; details?: string },
100
response?: Omit<NonNullable<UppyFile<M, B>['response']>, 'uploadURL'> | undefined
101
) => void;
102
}
103
```
104
105
**upload-progress**: Progress update for individual file upload
106
**progress**: Overall upload progress (0-100)
107
**upload-success**: File upload completed successfully
108
**upload-error**: File upload failed
109
110
### Upload State Events
111
112
```typescript { .api }
113
interface UppyEventMap<M extends Meta, B extends Body> {
114
'upload-pause': (file: UppyFile<M, B> | undefined, isPaused: boolean) => void;
115
'upload-retry': (file: UppyFile<M, B>) => void;
116
'upload-stalled': (
117
error: { message: string; details?: string },
118
files: UppyFile<M, B>[]
119
) => void;
120
}
121
```
122
123
**upload-pause**: File upload paused or resumed
124
**upload-retry**: File upload is being retried
125
**upload-stalled**: Upload appears to be stalled
126
127
### Processing Events
128
129
```typescript { .api }
130
interface UppyEventMap<M extends Meta, B extends Body> {
131
'preprocess-progress': (
132
file: UppyFile<M, B> | undefined,
133
progress: NonNullable<FileProgressStarted['preprocess']>
134
) => void;
135
'preprocess-complete': (
136
file: UppyFile<M, B> | undefined,
137
progress?: NonNullable<FileProgressStarted['preprocess']>
138
) => void;
139
'postprocess-progress': (
140
file: UppyFile<M, B> | undefined,
141
progress: NonNullable<FileProgressStarted['postprocess']>
142
) => void;
143
'postprocess-complete': (
144
file: UppyFile<M, B> | undefined,
145
progress?: NonNullable<FileProgressStarted['preprocess']>
146
) => void;
147
}
148
```
149
150
**preprocess-progress**: Progress update during file preprocessing
151
**preprocess-complete**: File preprocessing completed
152
**postprocess-progress**: Progress update during file postprocessing
153
**postprocess-complete**: File postprocessing completed
154
155
### Validation Events
156
157
```typescript { .api }
158
interface UppyEventMap<M extends Meta, B extends Body> {
159
'restriction-failed': (file: UppyFile<M, B> | undefined, error: Error) => void;
160
}
161
```
162
163
**restriction-failed**: File failed validation against restrictions
164
165
### State Management Events
166
167
```typescript { .api }
168
interface UppyEventMap<M extends Meta, B extends Body> {
169
'state-update': (
170
prevState: State<M, B>,
171
nextState: State<M, B>,
172
patch?: Partial<State<M, B>>
173
) => void;
174
}
175
```
176
177
**state-update**: Uppy state has changed
178
179
### Information Events
180
181
```typescript { .api }
182
interface UppyEventMap<M extends Meta, B extends Body> {
183
'info-visible': () => void;
184
'info-hidden': () => void;
185
}
186
```
187
188
**info-visible**: Info message is now visible to user
189
**info-hidden**: Info message has been hidden
190
191
### Network Events
192
193
```typescript { .api }
194
interface UppyEventMap<M extends Meta, B extends Body> {
195
'is-offline': () => void;
196
'is-online': () => void;
197
}
198
```
199
200
**is-offline**: Internet connection lost
201
**is-online**: Internet connection restored
202
203
### Plugin Events
204
205
```typescript { .api }
206
interface UppyEventMap<M extends Meta, B extends Body> {
207
'plugin-added': (plugin: UnknownPlugin<any, any>) => void;
208
'plugin-remove': (plugin: UnknownPlugin<any, any>) => void;
209
}
210
```
211
212
**plugin-added**: Plugin has been installed
213
**plugin-remove**: Plugin has been removed
214
215
### Restoration Events
216
217
```typescript { .api }
218
interface UppyEventMap<M extends Meta, B extends Body> {
219
restored: (pluginData: any) => void;
220
'restore-confirmed': () => void;
221
'restore-canceled': () => void;
222
}
223
```
224
225
**restored**: Upload state has been restored
226
**restore-confirmed**: User confirmed state restoration
227
**restore-canceled**: User canceled state restoration
228
229
## Event Usage Examples
230
231
### Basic Event Handling
232
233
```typescript
234
import Uppy from '@uppy/core';
235
236
const uppy = new Uppy();
237
238
// File lifecycle events
239
uppy.on('file-added', (file) => {
240
console.log('File added:', file.name, file.size);
241
});
242
243
uppy.on('file-removed', (file) => {
244
console.log('File removed:', file.name);
245
});
246
247
// Upload progress tracking
248
uppy.on('upload-progress', (file, progress) => {
249
if (file) {
250
console.log(`${file.name}: ${progress.bytesUploaded}/${progress.bytesTotal}`);
251
}
252
});
253
254
uppy.on('upload-success', (file, response) => {
255
console.log('Upload successful:', file?.name, response.body);
256
});
257
258
uppy.on('upload-error', (file, error, response) => {
259
console.error('Upload failed:', file?.name, error.message);
260
});
261
```
262
263
### Progress Monitoring
264
265
```typescript
266
let totalProgress = 0;
267
268
uppy.on('progress', (progress) => {
269
totalProgress = progress;
270
updateProgressBar(progress);
271
});
272
273
uppy.on('upload-start', (files) => {
274
console.log(`Starting upload of ${files.length} files`);
275
showProgressModal();
276
});
277
278
uppy.on('complete', (result) => {
279
console.log('Upload complete:', result);
280
hideProgressModal();
281
282
if (result.failed.length > 0) {
283
showErrorMessage(`${result.failed.length} files failed to upload`);
284
} else {
285
showSuccessMessage(`${result.successful.length} files uploaded successfully`);
286
}
287
});
288
289
function updateProgressBar(progress) {
290
const progressBar = document.querySelector('#progress-bar');
291
progressBar.style.width = `${progress}%`;
292
progressBar.textContent = `${Math.round(progress)}%`;
293
}
294
```
295
296
### File Validation Events
297
298
```typescript
299
uppy.on('restriction-failed', (file, error) => {
300
console.log('File restriction failed:', file?.name, error.message);
301
302
// Show custom error message to user
303
if (error.message.includes('size')) {
304
showNotification('File is too large', 'error');
305
} else if (error.message.includes('type')) {
306
showNotification('File type not allowed', 'error');
307
}
308
});
309
310
// Pre-upload validation
311
uppy.on('upload', (uploadID, files) => {
312
// Custom validation before upload starts
313
const invalidFiles = files.filter(file =>
314
!file.name.match(/^[a-zA-Z0-9_-]+\.[a-zA-Z0-9]+$/)
315
);
316
317
if (invalidFiles.length > 0) {
318
uppy.info('Some files have invalid names', 'error');
319
invalidFiles.forEach(file => uppy.removeFile(file.id));
320
}
321
});
322
```
323
324
### EventManager Usage
325
326
```typescript
327
import Uppy, { EventManager } from '@uppy/core';
328
329
class FileUploadComponent {
330
private uppy: Uppy;
331
private eventManager: EventManager<any, any>;
332
333
constructor() {
334
this.uppy = new Uppy();
335
this.eventManager = new EventManager(this.uppy);
336
this.setupEvents();
337
}
338
339
setupEvents() {
340
// All events registered through EventManager
341
this.eventManager.on('file-added', this.handleFileAdded);
342
this.eventManager.on('upload-success', this.handleUploadSuccess);
343
this.eventManager.on('upload-error', this.handleUploadError);
344
345
// File-specific event helpers
346
this.eventManager.onFilePause('specific-file-id', (isPaused) => {
347
console.log('File pause state changed:', isPaused);
348
});
349
}
350
351
handleFileAdded = (file) => {
352
// Update component state
353
this.updateFileList();
354
};
355
356
handleUploadSuccess = (file, response) => {
357
// Handle successful upload
358
this.processUploadResult(file, response);
359
};
360
361
handleUploadError = (file, error) => {
362
// Handle upload error
363
this.showErrorForFile(file, error);
364
};
365
366
destroy() {
367
// Clean up all event listeners at once
368
this.eventManager.remove();
369
this.uppy.destroy();
370
}
371
}
372
```
373
374
### State Monitoring
375
376
```typescript
377
uppy.on('state-update', (prevState, nextState, patch) => {
378
console.log('State updated:', patch);
379
380
// React to specific state changes
381
if (patch?.files) {
382
const fileIds = Object.keys(patch.files);
383
fileIds.forEach(fileId => {
384
const fileUpdate = patch.files[fileId];
385
if (fileUpdate.progress) {
386
updateFileProgress(fileId, fileUpdate.progress);
387
}
388
});
389
}
390
391
if (patch?.totalProgress !== undefined) {
392
updateOverallProgress(patch.totalProgress);
393
}
394
});
395
```
396
397
### Plugin Communication
398
399
```typescript
400
// Plugin emitting custom events
401
class MyPlugin extends BasePlugin {
402
doSomething() {
403
// Emit custom event
404
this.uppy.emit('my-plugin:action-completed', { data: 'result' });
405
}
406
}
407
408
// Listening for plugin events
409
uppy.on('my-plugin:action-completed', (result) => {
410
console.log('Plugin action completed:', result);
411
});
412
413
// Plugin lifecycle events
414
uppy.on('plugin-added', (plugin) => {
415
console.log('Plugin added:', plugin.id, plugin.type);
416
417
if (plugin.type === 'dashboard') {
418
// Configure dashboard plugin
419
plugin.setOptions({ theme: 'dark' });
420
}
421
});
422
```
423
424
### Network Status Handling
425
426
```typescript
427
uppy.on('is-offline', () => {
428
console.log('Connection lost');
429
uppy.info('Connection lost. Uploads will resume when back online.', 'warning');
430
pauseAllUploads();
431
});
432
433
uppy.on('is-online', () => {
434
console.log('Connection restored');
435
uppy.info('Connection restored. Resuming uploads.', 'success');
436
resumeAllUploads();
437
});
438
439
function pauseAllUploads() {
440
uppy.pauseAll();
441
showOfflineIndicator();
442
}
443
444
function resumeAllUploads() {
445
uppy.resumeAll();
446
hideOfflineIndicator();
447
}
448
```
449
450
## Event Best Practices
451
452
### Event Cleanup
453
Always remove event listeners when components are destroyed to prevent memory leaks:
454
455
```typescript
456
// Manual cleanup
457
const handler = (file) => console.log(file);
458
uppy.on('file-added', handler);
459
// Later...
460
uppy.off('file-added', handler);
461
462
// Or use EventManager for automatic cleanup
463
const eventManager = new EventManager(uppy);
464
eventManager.on('file-added', handler);
465
// Later...
466
eventManager.remove(); // Removes all managed listeners
467
```
468
469
### Error Handling
470
Wrap event handlers in try-catch blocks to prevent errors from breaking the upload process:
471
472
```typescript
473
uppy.on('upload-success', (file, response) => {
474
try {
475
processUploadResponse(file, response);
476
} catch (error) {
477
console.error('Error processing upload response:', error);
478
uppy.info('Upload succeeded but processing failed', 'warning');
479
}
480
});
481
```
482
483
### Performance Considerations
484
- Use debouncing for frequently fired events like `progress` and `upload-progress`
485
- Avoid heavy computations in event handlers
486
- Consider using `once()` for one-time event handling
487
488
```typescript
489
// Debounced progress handler
490
let progressTimeout;
491
uppy.on('progress', (progress) => {
492
clearTimeout(progressTimeout);
493
progressTimeout = setTimeout(() => {
494
updateProgressDisplay(progress);
495
}, 100);
496
});
497
```