or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

cloud-providers.mdcore-uppy.mdfile-sources.mdindex.mdplugin-architecture.mdtypescript-support.mdui-plugins.mdupload-handlers.mdutility-plugins.md

plugin-architecture.mddocs/

0

# Plugin Architecture

1

2

Uppy's plugin architecture provides a modular system for extending functionality through base classes and standardized interfaces. Plugins can add UI components, file sources, upload handlers, or utility functions to the core Uppy instance.

3

4

## Capabilities

5

6

### BasePlugin

7

8

Foundation class for all Uppy plugins providing core plugin functionality and lifecycle management.

9

10

```typescript { .api }

11

/**

12

* Base class for all Uppy plugins

13

* @template Options - Plugin configuration options type

14

* @template State - Plugin state type

15

* @template Events - Plugin events type

16

*/

17

abstract class BasePlugin<Options = {}, State = {}, Events = {}> {

18

/**

19

* Create plugin instance

20

* @param uppy - Uppy instance

21

* @param options - Plugin configuration options

22

*/

23

constructor(uppy: Uppy<any, any>, options?: Options);

24

25

/**

26

* Plugin identifier

27

*/

28

readonly id: string;

29

30

/**

31

* Plugin type identifier

32

*/

33

readonly type: string;

34

35

/**

36

* Reference to parent Uppy instance

37

*/

38

readonly uppy: Uppy<any, any>;

39

40

/**

41

* Plugin configuration options

42

*/

43

readonly opts: Options;

44

45

/**

46

* Install plugin - called when plugin is added to Uppy

47

*/

48

abstract install(): void;

49

50

/**

51

* Uninstall plugin - called when plugin is removed from Uppy

52

*/

53

abstract uninstall(): void;

54

55

/**

56

* Update plugin options

57

* @param newOptions - Partial options to merge

58

*/

59

protected setOptions(newOptions: Partial<Options>): void;

60

61

/**

62

* Get plugin-specific state

63

* @returns Current plugin state

64

*/

65

protected getPluginState(): State;

66

67

/**

68

* Update plugin-specific state

69

* @param patch - Partial state to merge

70

*/

71

protected setPluginState(patch: Partial<State>): void;

72

}

73

```

74

75

**Usage Example:**

76

77

```typescript

78

import { BasePlugin } from "uppy";

79

80

interface MyPluginOptions {

81

apiKey: string;

82

timeout?: number;

83

}

84

85

interface MyPluginState {

86

isConnected: boolean;

87

lastSync: number;

88

}

89

90

class MyPlugin extends BasePlugin<MyPluginOptions, MyPluginState> {

91

constructor(uppy, options) {

92

super(uppy, {

93

timeout: 5000,

94

...options

95

});

96

97

this.id = 'MyPlugin';

98

this.type = 'utility';

99

}

100

101

install() {

102

// Set initial state

103

this.setPluginState({

104

isConnected: false,

105

lastSync: 0

106

});

107

108

// Listen to Uppy events

109

this.uppy.on('file-added', this.handleFileAdded.bind(this));

110

}

111

112

uninstall() {

113

// Clean up event listeners

114

this.uppy.off('file-added', this.handleFileAdded.bind(this));

115

}

116

117

private handleFileAdded(file) {

118

console.log('File added:', file.name);

119

this.setPluginState({ lastSync: Date.now() });

120

}

121

}

122

123

// Use the plugin

124

const uppy = new Uppy();

125

uppy.use(MyPlugin, { apiKey: 'abc123' });

126

```

127

128

### UIPlugin

129

130

Extended base class for plugins that render user interface components, providing additional methods for DOM management.

131

132

```typescript { .api }

133

/**

134

* Base class for UI plugins that render components

135

* @template Options - Plugin configuration options type

136

* @template State - Plugin state type

137

*/

138

abstract class UIPlugin<Options = {}, State = {}> extends BasePlugin<Options, State> {

139

/**

140

* Create UI plugin instance

141

* @param uppy - Uppy instance

142

* @param options - Plugin configuration options

143

*/

144

constructor(uppy: Uppy<any, any>, options?: Options);

145

146

/**

147

* Render the plugin UI component

148

* @returns Preact component or element

149

*/

150

protected render(): ComponentChild;

151

152

/**

153

* Mount the plugin to a DOM target

154

* @param target - CSS selector or DOM element

155

* @param plugin - Plugin instance to mount

156

*/

157

protected mount(target: string | Element, plugin: UIPlugin<any, any>): void;

158

159

/**

160

* Unmount the plugin from DOM

161

*/

162

protected unmount(): void;

163

164

/**

165

* Force re-render of the plugin UI

166

*/

167

protected rerender(): void;

168

169

/**

170

* Get plugin DOM element

171

* @returns Plugin root element or null

172

*/

173

protected getElement(): Element | null;

174

}

175

```

176

177

**Usage Example:**

178

179

```typescript

180

import { UIPlugin } from "uppy";

181

import { h } from "preact";

182

183

interface FileCounterOptions {

184

target: string;

185

showTotal?: boolean;

186

}

187

188

class FileCounter extends UIPlugin<FileCounterOptions> {

189

constructor(uppy, options) {

190

super(uppy, {

191

showTotal: true,

192

...options

193

});

194

195

this.id = 'FileCounter';

196

this.type = 'ui';

197

}

198

199

install() {

200

const target = this.opts.target;

201

if (target) {

202

this.mount(target, this);

203

}

204

205

// Re-render on file changes

206

this.uppy.on('file-added', this.rerender.bind(this));

207

this.uppy.on('file-removed', this.rerender.bind(this));

208

}

209

210

uninstall() {

211

this.unmount();

212

this.uppy.off('file-added', this.rerender.bind(this));

213

this.uppy.off('file-removed', this.rerender.bind(this));

214

}

215

216

render() {

217

const files = this.uppy.getFiles();

218

const fileCount = files.length;

219

const totalSize = files.reduce((sum, file) => sum + file.size, 0);

220

221

return h('div', { className: 'file-counter' }, [

222

h('span', null, `Files: ${fileCount}`),

223

this.opts.showTotal && h('span', null, ` (${this.formatBytes(totalSize)})`)

224

]);

225

}

226

227

private formatBytes(bytes) {

228

if (bytes === 0) return '0 Bytes';

229

const k = 1024;

230

const sizes = ['Bytes', 'KB', 'MB', 'GB'];

231

const i = Math.floor(Math.log(bytes) / Math.log(k));

232

return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];

233

}

234

}

235

236

// Use the UI plugin

237

const uppy = new Uppy();

238

uppy.use(FileCounter, {

239

target: '#file-counter',

240

showTotal: true

241

});

242

```

243

244

### Plugin Lifecycle

245

246

Standard lifecycle methods and hooks for plugin development.

247

248

```typescript { .api }

249

/**

250

* Plugin lifecycle interface

251

*/

252

interface PluginLifecycle {

253

/**

254

* Called when plugin is added to Uppy instance

255

* Use for: event listeners, initial state, DOM setup

256

*/

257

install(): void;

258

259

/**

260

* Called when plugin is removed from Uppy instance

261

* Use for: cleanup, removing listeners, DOM cleanup

262

*/

263

uninstall(): void;

264

265

/**

266

* Called after all plugins are installed (optional)

267

* Use for: inter-plugin communication setup

268

*/

269

afterInstall?(): void;

270

271

/**

272

* Called before upload starts (optional)

273

* Use for: validation, file preprocessing

274

*/

275

prepareUpload?(): Promise<void>;

276

}

277

```

278

279

### Plugin State Management

280

281

Plugins maintain isolated state that integrates with Uppy's central state management.

282

283

```typescript { .api }

284

/**

285

* Plugin state management methods

286

*/

287

interface PluginStateManager<State> {

288

/**

289

* Get current plugin state

290

* @returns Plugin state object

291

*/

292

getPluginState(): State;

293

294

/**

295

* Update plugin state with patch

296

* @param patch - Partial state to merge

297

*/

298

setPluginState(patch: Partial<State>): void;

299

300

/**

301

* Reset plugin state to defaults

302

*/

303

resetPluginState(): void;

304

}

305

```

306

307

**Usage Example:**

308

309

```typescript

310

interface UploadStatsState {

311

uploadCount: number;

312

totalBytes: number;

313

averageSpeed: number;

314

}

315

316

class UploadStats extends BasePlugin<{}, UploadStatsState> {

317

install() {

318

// Initialize state

319

this.setPluginState({

320

uploadCount: 0,

321

totalBytes: 0,

322

averageSpeed: 0

323

});

324

325

this.uppy.on('upload-success', this.trackUpload.bind(this));

326

}

327

328

private trackUpload(file, response) {

329

const currentState = this.getPluginState();

330

const newSpeed = this.calculateSpeed(file.size, file.progress.uploadStarted);

331

332

this.setPluginState({

333

uploadCount: currentState.uploadCount + 1,

334

totalBytes: currentState.totalBytes + file.size,

335

averageSpeed: (currentState.averageSpeed + newSpeed) / 2

336

});

337

}

338

339

getStats() {

340

return this.getPluginState();

341

}

342

}

343

```

344

345

### Plugin Events

346

347

Plugins can emit and listen to custom events through the Uppy event system.

348

349

```typescript { .api }

350

/**

351

* Plugin event handling

352

*/

353

interface PluginEventEmitter {

354

/**

355

* Emit plugin-specific event

356

* @param event - Event name (should be prefixed with plugin ID)

357

* @param data - Event data

358

*/

359

emit(event: string, ...data: any[]): void;

360

361

/**

362

* Listen to Uppy or other plugin events

363

* @param event - Event name

364

* @param handler - Event handler function

365

*/

366

on(event: string, handler: (...args: any[]) => void): void;

367

368

/**

369

* Remove event listener

370

* @param event - Event name

371

* @param handler - Event handler to remove

372

*/

373

off(event: string, handler: (...args: any[]) => void): void;

374

}

375

```

376

377

**Usage Example:**

378

379

```typescript

380

class NotificationPlugin extends BasePlugin {

381

install() {

382

// Listen to upload events

383

this.uppy.on('upload-success', this.showSuccess.bind(this));

384

this.uppy.on('upload-error', this.showError.bind(this));

385

386

// Listen to custom plugin events

387

this.uppy.on('notification:show', this.displayNotification.bind(this));

388

}

389

390

private showSuccess(file) {

391

// Emit custom event

392

this.uppy.emit('notification:show', {

393

type: 'success',

394

message: `${file.name} uploaded successfully`,

395

timeout: 3000

396

});

397

}

398

399

private showError(file, error) {

400

this.uppy.emit('notification:show', {

401

type: 'error',

402

message: `Failed to upload ${file.name}: ${error.message}`,

403

timeout: 5000

404

});

405

}

406

407

private displayNotification(notification) {

408

// Display notification in UI

409

console.log(`[${notification.type.toUpperCase()}] ${notification.message}`);

410

411

if (notification.timeout) {

412

setTimeout(() => {

413

this.uppy.emit('notification:hide', notification);

414

}, notification.timeout);

415

}

416

}

417

}

418

```

419

420

### Plugin Options and Defaults

421

422

Standard pattern for handling plugin configuration with defaults.

423

424

```typescript { .api }

425

/**

426

* Plugin options management

427

*/

428

interface PluginOptionsHandler<Options> {

429

/**

430

* Default options for the plugin

431

*/

432

readonly defaultOptions: Options;

433

434

/**

435

* Update plugin options after instantiation

436

* @param newOptions - Partial options to merge

437

*/

438

setOptions(newOptions: Partial<Options>): void;

439

440

/**

441

* Get current plugin options

442

* @returns Current options object

443

*/

444

getOptions(): Options;

445

}

446

```

447

448

**Usage Example:**

449

450

```typescript

451

interface CompressionOptions {

452

quality: number;

453

maxWidth: number;

454

maxHeight: number;

455

mimeType: string;

456

}

457

458

class ImageCompression extends BasePlugin<CompressionOptions> {

459

constructor(uppy, options) {

460

const defaultOptions: CompressionOptions = {

461

quality: 0.8,

462

maxWidth: 1920,

463

maxHeight: 1080,

464

mimeType: 'image/jpeg'

465

};

466

467

super(uppy, {

468

...defaultOptions,

469

...options

470

});

471

}

472

473

// Update compression quality dynamically

474

setQuality(quality: number) {

475

this.setOptions({ quality });

476

}

477

478

install() {

479

this.uppy.on('preprocess-progress', this.compressImage.bind(this));

480

}

481

482

private compressImage(file) {

483

if (!file.type.startsWith('image/')) return;

484

485

const { quality, maxWidth, maxHeight, mimeType } = this.opts;

486

// Compression logic here...

487

}

488

}

489

```

490

491

## Core Plugin Types

492

493

```typescript { .api }

494

/**

495

* Plugin type categories

496

*/

497

type PluginType =

498

| 'ui' // User interface plugins (Dashboard, FileInput, etc.)

499

| 'source' // File source plugins (GoogleDrive, Webcam, etc.)

500

| 'uploader' // Upload handler plugins (Tus, XHRUpload, etc.)

501

| 'utility' // Utility plugins (ThumbnailGenerator, Form, etc.)

502

| 'custom'; // Custom plugin types

503

504

/**

505

* Plugin constructor interface

506

*/

507

interface PluginConstructor<Options = {}, State = {}> {

508

new (uppy: Uppy<any, any>, options?: Options): BasePlugin<Options, State>;

509

}

510

511

/**

512

* Plugin registration options

513

*/

514

interface PluginRegistration<Options> {

515

id: string;

516

type: PluginType;

517

options?: Options;

518

}

519

```