> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://uploadcare.com/docs/llms.txt.

# Plugin structure & registration

> How to shape a plugin, register it with the uploader, and clean it up safely.

## Plugin structure

A plugin is an object with a stable `id` and a `setup` function:

```typescript
import type { UploaderPlugin } from '@uploadcare/file-uploader';

export const myPlugin: UploaderPlugin = {
  id: 'my-plugin',
  setup({ pluginApi, uploaderApi }) {
    // register everything here
  },
};
```

Treat `id` as the plugin's identity within a single uploader context. It must be unique for that context.

The `setup` function receives a single object with the following properties:

| Property      | Type                                                         | Description                                                                              |
| ------------- | ------------------------------------------------------------ | ---------------------------------------------------------------------------------------- |
| `pluginApi`   | [`PluginApi`](/docs/file-uploader/plugins/extension-points/) | Plugin-specific API for registration, config, activity state, and file updates           |
| `uploaderApi` | [`UploaderPublicApi`](/docs/file-uploader/api/)              | The uploader's public runtime API for actions such as adding files or opening activities |

`setup` can be synchronous or async. It can also return a disposer function that runs when the plugin is unregistered.

## Registering plugins

Pass plugins to the `<uc-config>` element via its `plugins` property:

```html
<uc-config id="cfg" ctx-name="my-uploader" pubkey="YOUR_PUBLIC_KEY"></uc-config>
<uc-file-uploader-regular ctx-name="my-uploader"></uc-file-uploader-regular>

<script type="module">
  import { myPlugin } from './my-plugin.js';

  const cfg = document.querySelector('#cfg');
  cfg.plugins = [myPlugin];
</script>
```

Or in a framework (React example):

```tsx
import { FileUploaderRegular } from '@uploadcare/react-uploader';
import { myPlugin } from './my-plugin';

export function Uploader() {
  return <FileUploaderRegular pubkey="YOUR_PUBLIC_KEY" plugins={[myPlugin]} />;
}
```

> **Note**
>
> **Note:** The uploader's [built-in plugins](/docs/file-uploader/plugins/built-in/) (camera, image editor, external sources, and others) are managed separately and merged in automatically based on your config, so setting `plugins` only affects your custom plugins.

Plugins are initialized in array order. Replacing `cfg.plugins` causes the uploader to resync the list: removed plugins are disposed, new plugins are set up, and duplicates by `id` are skipped.

> **Warning**
>
> **Warning:** To add or remove a plugin at runtime, assign a new array to `plugins`. Mutating the existing array in place — for example with `push()` or `splice()` — is **not supported** and produces non-deterministic results: the mutation may occasionally be picked up due to a timing race with async lazy-plugin loading, but this is not guaranteed and will silently break under different conditions. Always replace the array:
>
> ```ts
> cfg.plugins = [...cfg.plugins, newPlugin];
> ```

## Cleanup and disposal

If `setup` returns a disposer function, it runs when the plugin is removed from the `plugins` array or when the uploader is destroyed.

```typescript
const myPlugin: UploaderPlugin = {
  id: 'my-plugin',
  setup({ pluginApi }) {
    const interval = setInterval(poll, 5000);

    return () => {
      clearInterval(interval);
    };
  },
};
```

Subscriptions created with `pluginApi.config.subscribe` and `pluginApi.activity.subscribeToParams` inside `setup` are cleaned up automatically when the plugin is unregistered. If you create subscriptions inside an activity's `render` function, unsubscribe them in the cleanup function returned from `render` so they do not outlive that activity mount.