Core module for the extensible JavaScript file upload widget with support for drag&drop, resumable uploads, previews, restrictions, file processing/encoding, remote providers like Instagram, Dropbox, Google Drive, S3 and more
—
Uppy's plugin system allows extending functionality through BasePlugin and UIPlugin classes. Plugins can add new upload sources, processing capabilities, UI components, or modify upload behavior.
Base class for all Uppy plugins, providing core functionality without DOM rendering capabilities.
constructor(uppy: Uppy<M, B>, opts?: Opts)Parameters:
uppy: Uppy instance the plugin will be attached toopts: Plugin-specific configuration optionsuppy: Uppy<M, B>; // Uppy instance reference
opts: Opts; // Plugin options
id: string; // Plugin identifier
type: string; // Plugin type
VERSION: string; // Plugin version
defaultLocale: OptionalPluralizeLocale; // Default locale
i18n: I18n; // Translation function
i18nArray: Translator['translateArray']; // Array translationgetPluginState(): PluginState;
setPluginState(update?: Partial<PluginState>): void;Usage:
getPluginState(): Returns plugin's slice of global statesetPluginState(): Updates plugin state and triggers UI re-rendersetOptions(newOpts: Partial<Opts>): void;Updates plugin options and triggers localization re-initialization.
i18nInit(): void;Initializes translation functions with locale hierarchy: default locale → Uppy locale → plugin locale.
install(): void;
uninstall(): void;
update(state: Partial<State<M, B>>): void;
afterUpdate(): void;Hook Descriptions:
install(): Called when plugin is added to Uppy instanceuninstall(): Called when plugin is removed from Uppy instanceupdate(state): Called on every state change with state patchafterUpdate(): Called after all updates complete (debounced)addTarget(plugin: UnknownPlugin<M, B>): HTMLElement | null;Override this method to integrate with other plugins that provide UI targets.
Extended plugin class with Preact rendering capabilities for user interface components.
constructor(uppy: Uppy<M, B>, opts?: UIPluginOptions)Inherits all BasePlugin functionality with additional UI-specific options.
el: HTMLElement | null; // DOM element reference
isTargetDOMEl: boolean; // Whether target is DOM element
parent: unknown; // Parent component referenceabstract render(): ComponentChild;Must be implemented by subclasses to define the plugin's UI structure using Preact components.
mount(target: PluginTarget, plugin: UnknownPlugin<M, B>): void;
unmount(): void;Parameters:
target: DOM selector, element, or plugin to mount toplugin: Plugin instance requesting the mountaddTarget(plugin: UnknownPlugin<M, B>): HTMLElement | null;Returns DOM element where other plugins can mount their UI.
interface PluginOpts {
locale?: OptionalPluralizeLocale;
id?: string;
}interface UIPluginOptions extends PluginOpts {
target?: PluginTarget;
}
type PluginTarget =
| string // CSS selector
| Element // DOM element
| UnknownPlugin<any, any> // Another plugintype DefinePluginOpts<
Opts,
AlwaysDefinedKeys extends keyof OnlyOptionals<Opts>
> = Opts & Required<Pick<Opts, AlwaysDefinedKeys>>;Use DefinePluginOpts to mark certain optional properties as required for your plugin.
type UnknownPlugin<
M extends Meta,
B extends Body,
PluginState extends Record<string, unknown> = Record<string, unknown>
> = BasePlugin<any, M, B, PluginState>;import { BasePlugin, type PluginOpts } from '@uppy/core';
interface MyPluginOptions extends PluginOpts {
apiKey?: string;
timeout?: number;
}
interface MyPluginState {
isProcessing: boolean;
lastResult?: any;
}
class MyPlugin extends BasePlugin<MyPluginOptions, Meta, Body, MyPluginState> {
static VERSION = '1.0.0';
type = 'myPlugin';
id = 'MyPlugin';
defaultOptions: Partial<MyPluginOptions> = {
timeout: 30000
};
constructor(uppy, opts) {
super(uppy, { ...this.defaultOptions, ...opts });
this.id = this.opts.id || this.id;
}
install() {
// Setup event listeners
this.uppy.on('file-added', this.handleFileAdded);
}
uninstall() {
// Cleanup
this.uppy.off('file-added', this.handleFileAdded);
}
handleFileAdded = (file) => {
this.setPluginState({ isProcessing: true });
// Process file
this.processFile(file).then(result => {
this.setPluginState({
isProcessing: false,
lastResult: result
});
});
};
async processFile(file) {
// Custom processing logic
return new Promise(resolve => {
setTimeout(() => resolve({ processed: true }), 1000);
});
}
}
export default MyPlugin;import { UIPlugin, type UIPluginOptions } from '@uppy/core';
import { h } from 'preact';
interface MyUIPluginOptions extends UIPluginOptions {
buttonText?: string;
}
class MyUIPlugin extends UIPlugin<MyUIPluginOptions, Meta, Body> {
static VERSION = '1.0.0';
type = 'myUIPlugin';
id = 'MyUIPlugin';
defaultOptions: Partial<MyUIPluginOptions> = {
buttonText: 'Process Files'
};
constructor(uppy, opts) {
super(uppy, { ...this.defaultOptions, ...opts });
this.id = this.opts.id || this.id;
}
render() {
const { buttonText } = this.opts;
const files = this.uppy.getFiles();
return h('div', { className: 'my-plugin' }, [
h('h3', null, 'My Custom Plugin'),
h('p', null, `Files: ${files.length}`),
h('button', {
onClick: this.handleButtonClick,
disabled: files.length === 0
}, buttonText)
]);
}
install() {
const { target } = this.opts;
if (target) {
this.mount(target, this);
}
}
uninstall() {
this.unmount();
}
handleButtonClick = () => {
const files = this.uppy.getFiles();
console.log('Processing files:', files);
// Custom processing logic
files.forEach(file => {
this.uppy.setFileState(file.id, {
meta: { ...file.meta, processed: true }
});
});
};
}
export default MyUIPlugin;import { BasePlugin } from '@uppy/core';
interface ProviderPluginOptions extends PluginOpts {
companionUrl: string;
companionHeaders?: Record<string, string>;
}
class MyProviderPlugin extends BasePlugin<ProviderPluginOptions, Meta, Body> {
static VERSION = '1.0.0';
type = 'provider';
id = 'MyProvider';
constructor(uppy, opts) {
super(uppy, opts);
this.requests = new Map();
}
install() {
this.uppy.addUploader(this.uploadFiles);
}
uninstall() {
this.uppy.removeUploader(this.uploadFiles);
// Cancel any ongoing requests
this.requests.forEach(request => request.abort());
this.requests.clear();
}
uploadFiles = async (fileIDs, uploadID) => {
const files = fileIDs.map(id => this.uppy.getFile(id));
for (const file of files) {
try {
await this.uploadFile(file, uploadID);
} catch (error) {
this.uppy.emit('upload-error', file, error);
}
}
};
async uploadFile(file, uploadID) {
const { companionUrl, companionHeaders } = this.opts;
// Implementation would interact with Companion server
const response = await fetch(`${companionUrl}/upload`, {
method: 'POST',
headers: companionHeaders,
body: file.data
});
if (!response.ok) {
throw new Error(`Upload failed: ${response.statusText}`);
}
const result = await response.json();
this.uppy.emit('upload-success', file, { body: result });
}
}
export default MyProviderPlugin;import Uppy from '@uppy/core';
import MyPlugin from './MyPlugin';
import MyUIPlugin from './MyUIPlugin';
const uppy = new Uppy()
.use(MyPlugin, {
apiKey: 'your-api-key',
timeout: 60000
})
.use(MyUIPlugin, {
target: '#my-plugin-container',
buttonText: 'Custom Process'
});
// Access plugin instances
const myPlugin = uppy.getPlugin('MyPlugin');
const myUIPlugin = uppy.getPlugin('MyUIPlugin');
// Plugin state access
console.log(myPlugin.getPluginState());getPluginState() and setPluginState() for plugin-specific stateuninstall()this contextuppy.info() for user-facing error messagesdefaultLocale with all translatable stringsthis.i18n() for single strings, this.i18nArray() for arraysafterUpdate() for expensive operations that don't need immediate executionuninstall() to prevent memory leaksInstall with Tessl CLI
npx tessl i tessl/npm-uppy--core