---
description: Register and control plugins in RealtimeKit meetings.
title: Plugins
image: https://developers.cloudflare.com/og-docs.png
---

[Skip to content](#main-content)

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/realtime/llms.txt  
> Use this file to discover all available pages before exploring further.

# Plugins

Last updated Jul 9, 2026|Copy as Markdown|[View as Markdown](https://developers.cloudflare.com/realtime/realtimekit/core/plugins/index.md)|[Agent setup](https://developers.cloudflare.com/agent-setup/)

This guide explains how to register, activate, and render plugins in a meeting using the Cloudflare RealtimeKit Core SDK.

Plugins are interactive real-time applications that run inside a meeting, such as a shared whiteboard or a document viewer. When a participant activates a plugin, it becomes active for everyone in the session.

This page is not available for the **Flutter**platform.

WebMobile

ReactWeb ComponentsAngular

Note

You must register a plugin with the same `id` on every platform your participants use. A plugin is only visible to a participant who has registered it, and activation state only syncs across participants who share the same plugin `id`. If a web participant registers a plugin under one `id` and a mobile participant uses a different `id`, the plugin will not be visible or synced across both platforms.

## The Plugins module

The meeting plugins object is available at `meeting.plugins`. It exposes two collections of [Plugin](#the-plugin-object) objects:

* `all`: every plugin available to the local participant.
* `active`: the plugins that are currently running in the session.

```js
// All plugins available to you
const allPlugins = meeting.plugins.all.toArray();

// Plugins currently active in the session
const activePlugins = meeting.plugins.active.toArray();

// Get a single plugin by its id
const plugin = meeting.plugins.all.get(pluginId);
```

Use the `useRealtimeKitSelector` hook to read plugins reactively. The hook only works when your component tree is wrapped in `RealtimeKitProvider`.

```jsx
import { useRealtimeKitSelector } from "@cloudflare/realtimekit-react";

const allPlugins = useRealtimeKitSelector((m) => m.plugins.all.toArray());
const activePlugins = useRealtimeKitSelector((m) => m.plugins.active.toArray());
```

```kotlin
// All plugins available to you
val allPlugins = meeting.plugins.all

// Plugins currently active in the session
val activePlugins = meeting.plugins.active

// Get a single plugin by its id
val plugin = meeting.plugins.all.firstOrNull { it.id == pluginId }
```

```swift
// All plugins available to you
let allPlugins = meeting.plugins.all

// Plugins currently active in the session
let activePlugins = meeting.plugins.active

// Get a single plugin by its id
let plugin = meeting.plugins.all.first { $0.id == pluginId }
```

Use the `useRealtimeKitSelector` hook to read plugins reactively.

```tsx
import { useRealtimeKitSelector } from "@cloudflare/realtimekit-react-native";

const allPlugins = useRealtimeKitSelector((m) => m.plugins.all.toArray());
const activePlugins = useRealtimeKitSelector((m) => m.plugins.active.toArray());

// Get a single plugin by its namespaced id
const plugin = useRealtimeKitSelector((m) => m.plugins.all.get(pluginId));
```

## Register a plugin

You register the plugins available in a session when you initialize the SDK. Each configuration provides the metadata RealtimeKit uses to list the plugin and the location it loads.

Pass an array of plugin configurations as `defaults.plugins`. Each configuration provides the metadata RealtimeKit uses to list the plugin and the `component` it renders.

```ts
RealtimeKitClient.init({
	authToken: "<auth_token>",
	defaults: {
		plugins: [
			{
				// User-provided unique id. The SDK prefixes it with
				// `{meetingId}:` to create the namespaced `plugin.id`.
				id: "whiteboard",
				// Display name shown in the plugins panel
				name: "Whiteboard",
				// Icon URL or data URI shown next to the name
				icon: "https://example.com/whiteboard.png",
				// Per-plugin permissions for the local participant
				permissions: {
					canActivate: true,
					canDeactivate: true,
				},
				// The element rendered when the plugin is active
				component: document.createElement("my-whiteboard"),
			},
		],
	},
});
```

The `component` is an `HTMLElement`. The [rtk-plugin-main](https://developers.cloudflare.com/realtime/realtimekit/ui-kit/api-reference/core/rtk-plugin-main/) component projects it into the meeting layout, so your application styles continue to apply.

Each plugin configuration accepts the following fields:

| Field       | Description                                                                                | Type                                             | Required |
| ----------- | ------------------------------------------------------------------------------------------ | ------------------------------------------------ | -------- |
| id          | Unique identifier for the plugin. The SDK prefixes it with {meetingId}: to form plugin.id. | string                                           | true     |
| name        | Display name shown in the plugins panel.                                                   | string                                           | true     |
| icon        | Icon URL or data URI shown next to the name.                                               | string                                           | true     |
| permissions | Controls whether the local participant can activate or deactivate the plugin.              | { canActivate: boolean; canDeactivate: boolean } | true     |
| component   | Element rendered when the plugin is active.                                                | HTMLElement                                      | true     |

Pass plugin configurations as `defaults.plugins` when calling `initMeeting`. In React Native, `component` should be an object with a `src` property containing the URL to render — you can load this in a `WebView` or use the UI Kit to handle rendering automatically.

```tsx
const [meeting, initMeeting] = useRealtimeKitClient();

await initMeeting({
  authToken: "<auth_token>",
  defaults: {
    plugins: [
      {
        // User-provided unique id. The SDK prefixes it with
        // `{meetingId}:` to create the namespaced `plugin.id`.
        id: "whiteboard",
        // Display name shown in the plugins panel
        name: "Whiteboard",
        // Icon URL shown next to the name
        icon: "https://example.com/whiteboard.png",
        // Per-plugin permissions for the local participant
        permissions: {
          canActivate: true,
          canDeactivate: true,
        },
        // URL to render when the plugin is active.
        // Pass an object with a src property: { src: string }.
        component: { src: "https://example.com/whiteboard/" },
      },
    ],
  },
});
```

Each plugin configuration accepts the following fields:

| Field       | Description                                                                                | Type                                             | Required |
| ----------- | ------------------------------------------------------------------------------------------ | ------------------------------------------------ | -------- |
| id          | Unique identifier for the plugin. The SDK prefixes it with {meetingId}: to form plugin.id. | string                                           | true     |
| name        | Display name shown in the plugins panel.                                                   | string                                           | true     |
| icon        | Icon URL shown next to the name.                                                           | string                                           | true     |
| permissions | Controls whether the local participant can activate or deactivate the plugin.              | { canActivate: boolean; canDeactivate: boolean } | true     |
| component   | Object with a src URL to render when the plugin is active.                                 | { src: string }                                  | true     |

Pass a `pluginConfigs` list to `RtkMeetingInfo`. The SDK loads each plugin's `url` directly into a WebView when the plugin is activated.

```kotlin
val meetingInfo = RtkMeetingInfo(
	authToken = "<auth_token>",
	pluginConfigs = listOf(
		RtkClientPluginConfig(
			// User-provided unique id. The SDK prefixes it with
			// `{meetingId}:` to create the namespaced `plugin.id`.
			id = "whiteboard",
			// Display name shown in the plugins list
			name = "Whiteboard",
			// Icon URL shown next to the name
			icon = "https://example.com/whiteboard.png",
			// URL loaded into the plugin WebView when the plugin is active
			url = "https://example.com/whiteboard/",
			// Per-plugin permissions for the local participant
			permissions = RtkClientPluginPermissions(
				canActivate = true,
				canDeactivate = true,
			),
		),
	),
)
```

Each plugin configuration accepts the following fields:

| Field       | Description                                                                                | Type                       | Required |
| ----------- | ------------------------------------------------------------------------------------------ | -------------------------- | -------- |
| id          | Unique identifier for the plugin. The SDK prefixes it with {meetingId}: to form plugin.id. | String                     | true     |
| name        | Display name shown in the plugins list.                                                    | String                     | true     |
| icon        | Icon URL shown next to the name.                                                           | String                     | true     |
| url         | URL loaded into the plugin WebView when the plugin is active.                              | String                     | true     |
| permissions | Controls whether the local participant can activate or deactivate the plugin.              | RtkClientPluginPermissions | true     |

Pass a `pluginConfigs` array to `RtkMeetingInfo`. The SDK loads each plugin's `url` directly into a WebView when the plugin is activated.

```swift
let meetingInfo = RtkMeetingInfo(
	authToken: "<auth_token>",
	pluginConfigs: [
		RtkClientPluginConfig(
			// User-provided unique id. The SDK prefixes it with
			// `{meetingId}:` to create the namespaced `plugin.id`.
			id: "whiteboard",
			// Display name shown in the plugins list
			name: "Whiteboard",
			// Icon URL shown next to the name
			icon: "https://example.com/whiteboard.png",
			// URL loaded into the plugin WebView when the plugin is active
			url: "https://example.com/whiteboard/",
			// Per-plugin permissions for the local participant
			permissions: RtkClientPluginPermissions(
				canActivate: true,
				canDeactivate: true
			)
		)
	]
)
```

Each plugin configuration accepts the following fields:

| Field       | Description                                                                                | Type                       | Required |
| ----------- | ------------------------------------------------------------------------------------------ | -------------------------- | -------- |
| id          | Unique identifier for the plugin. The SDK prefixes it with {meetingId}: to form plugin.id. | String                     | true     |
| name        | Display name shown in the plugins list.                                                    | String                     | true     |
| icon        | Icon URL shown next to the name.                                                           | String                     | true     |
| url         | URL loaded into the plugin WebView when the plugin is active.                              | String                     | true     |
| permissions | Controls whether the local participant can activate or deactivate the plugin.              | RtkClientPluginPermissions | true     |

## Activate and deactivate a plugin

Activation lives on the `Plugin` object. Calling `activate()` enables the plugin for every participant in the session, and `deactivate()` disables it for everyone. Both methods respect the plugin's `permissions`.

```js
const plugin = meeting.plugins.all.get(pluginId);

// Activate for all participants
await plugin.activate();

// Deactivate for all participants
await plugin.deactivate();
```

```jsx
const plugins = useRealtimeKitSelector((m) => m.plugins);

const plugin = plugins.all.get(pluginId);

// Activate for all participants
await plugin.activate();

// Deactivate for all participants
await plugin.deactivate();
```

```kotlin
val plugin = meeting.plugins.all.firstOrNull { it.id == pluginId } ?: return

// Activate for all participants
plugin.activate()

// Deactivate for all participants
plugin.deactivate()
```

```swift
guard let plugin = meeting.plugins.all.first(where: { $0.id == pluginId }) else { return }

// Activate for all participants
plugin.activate()

// Deactivate for all participants
plugin.deactivate()
```

Note

A participant can only activate a plugin when `permissions.canActivate` is `true`. A participant can deactivate a plugin when `permissions.canDeactivate` is `true`, or when they are the participant who activated it.

## The Plugin object

A `Plugin` object represents a single plugin. You obtain it from either collection in `meeting.plugins`.

| Property    | Description                                               | Type                                             |
| ----------- | --------------------------------------------------------- | ------------------------------------------------ |
| id          | Namespaced plugin id, in the form {meetingId}:{configId}. | string                                           |
| name        | Display name of the plugin.                               | string                                           |
| icon        | Icon URL or data URI.                                     | string                                           |
| permissions | Activation permissions for the local participant.         | { canActivate: boolean; canDeactivate: boolean } |
| component   | Element rendered when the plugin is active.               | HTMLElement                                      |
| active      | Whether the plugin is currently running.                  | boolean                                          |
| enabledBy   | Id of the participant who activated the plugin.           | string                                           |

| Property    | Description                                               | Type                       |
| ----------- | --------------------------------------------------------- | -------------------------- |
| id          | Namespaced plugin id, in the form {meetingId}:{configId}. | String                     |
| name        | Display name of the plugin.                               | String                     |
| icon        | Icon URL.                                                 | String                     |
| permissions | Activation permissions for the local participant.         | RtkClientPluginPermissions |

While a plugin is active, call `getPluginView()` to obtain the Android `WebView` that hosts it, and `sendData(eventName, data)` to push data into that WebView.

| Property    | Description                                               | Type                       |
| ----------- | --------------------------------------------------------- | -------------------------- |
| id          | Namespaced plugin id, in the form {meetingId}:{configId}. | String                     |
| name        | Display name of the plugin.                               | String                     |
| icon        | Icon URL.                                                 | String                     |
| permissions | Activation permissions for the local participant.         | RtkClientPluginPermissions |

While a plugin is active, call `getPluginView()` to obtain the `WKWebView` that hosts it, and `sendData(eventName:data:)` to push data into that WebView.

| Property    | Description                                                                              | Type                                             |
| ----------- | ---------------------------------------------------------------------------------------- | ------------------------------------------------ |
| id          | Namespaced plugin id, in the form {meetingId}:{configId}.                                | string                                           |
| name        | Display name of the plugin.                                                              | string                                           |
| icon        | Icon URL.                                                                                | string                                           |
| permissions | Activation permissions for the local participant.                                        | { canActivate: boolean; canDeactivate: boolean } |
| component   | The object provided at registration. Contains a src URL that your rendering layer loads. | { src: string }                                  |
| active      | Whether the plugin is currently running.                                                 | boolean                                          |
| enabledBy   | Id of the participant who activated the plugin.                                          | string                                           |

## Listen to plugin events

A `Plugin` object emits events as its state changes. You can listen on a single plugin, or on a map to receive events for every plugin it contains.

| Event       | Description                                                       |
| ----------- | ----------------------------------------------------------------- |
| stateUpdate | Emitted when the plugin is activated or deactivated.              |
| enabled     | Emitted when the plugin becomes active for the local participant. |
| closed      | Emitted when the plugin is deactivated for the local participant. |
| ready       | Emitted when the plugin is ready to use.                          |

```js
const plugin = meeting.plugins.all.get(pluginId);

plugin.on("stateUpdate", ({ active, pluginId }) => {
	console.log(`Plugin ${pluginId} active:`, active);
});

// Listen for any plugin being added to or removed from the map
meeting.plugins.all.on("pluginAdded", (plugin) => {
	console.log("Plugin added:", plugin.name);
});

meeting.plugins.all.on("pluginDeleted", (plugin) => {
	console.log("Plugin removed:", plugin.name);
});
```

Register an `RtkPluginsEventListener` to receive plugin events.

| Callback            | Description                                               |
| ------------------- | --------------------------------------------------------- |
| onPluginActivated   | Called when a plugin is activated for all participants.   |
| onPluginDeactivated | Called when a plugin is deactivated for all participants. |
| onPluginMessage     | Called when a plugin sends a message to the app.          |
| onPluginFileRequest | Called when a plugin requests a file from the app.        |

```kotlin
val pluginsEventListener = object : RtkPluginsEventListener {
	override fun onPluginActivated(plugin: RtkPlugin) {
		// A plugin became active for all participants
	}

	override fun onPluginDeactivated(plugin: RtkPlugin) {
		// A plugin was deactivated for all participants
	}

	override fun onPluginMessage(plugin: RtkPlugin, eventName: String, data: Any?) {
		// A plugin sent a message to the app
	}

	override fun onPluginFileRequest(plugin: RtkPlugin) {
		// A plugin requested a file from the app
	}
}

meeting.addPluginsEventListener(pluginsEventListener)
```

Conform to `RtkPluginsEventListener` and register the listener to receive plugin events.

| Callback            | Description                                               |
| ------------------- | --------------------------------------------------------- |
| onPluginActivated   | Called when a plugin is activated for all participants.   |
| onPluginDeactivated | Called when a plugin is deactivated for all participants. |
| onPluginMessage     | Called when a plugin sends a message to the app.          |
| onPluginFileRequest | Called when a plugin requests a file from the app.        |

```swift
extension MeetingViewModel: RtkPluginsEventListener {
	func onPluginActivated(plugin: RtkPlugin) {
		// A plugin became active for all participants
	}

	func onPluginDeactivated(plugin: RtkPlugin) {
		// A plugin was deactivated for all participants
	}

	func onPluginMessage(plugin: RtkPlugin, eventName: String, data: Any?) {
		// A plugin sent a message to the app
	}

	func onPluginFileRequest(plugin: RtkPlugin) {
		// A plugin requested a file from the app
	}
}

meeting.addPluginsEventListener(self)
```

## Render plugins

If you use the UI Kit, RealtimeKit provides ready-made components for plugins:

* [rtk-plugins-toggle](https://developers.cloudflare.com/realtime/realtimekit/ui-kit/api-reference/core/rtk-plugins-toggle/): a control bar button that opens and closes the plugins sidebar.
* [rtk-plugins](https://developers.cloudflare.com/realtime/realtimekit/ui-kit/api-reference/core/rtk-plugins/): a list of available plugins with controls to activate or deactivate each one.
* [rtk-plugin-main](https://developers.cloudflare.com/realtime/realtimekit/ui-kit/api-reference/core/rtk-plugin-main/): renders the `component` of an active plugin in the meeting layout.

These components read from `meeting.plugins`, so they reflect plugin state automatically once you register your plugins at initialization.

When a plugin is active, `getPluginView()` returns the Android `WebView` that hosts it. Add this view to your layout to display the plugin.

```kotlin
val plugin = meeting.plugins.active.firstOrNull() ?: return

// Returns an Android WebView you can add to your layout
val pluginView = plugin.getPluginView()
```

When a plugin is active, `getPluginView()` returns the `WKWebView` that hosts it. Add this view to your view hierarchy to display the plugin.

```swift
guard let plugin = meeting.plugins.active.first else { return }

// Returns a WKWebView you can add to your view hierarchy
let pluginView = plugin.getPluginView()
```

The React Native UI Kit provides ready-made components for plugins:

* [RtkPluginsToggle](https://developers.cloudflare.com/realtime/realtimekit/ui-kit/api-reference/react-native/rtkpluginstoggle/): a control bar button that opens and closes the plugins sidebar.
* [RtkPlugins](https://developers.cloudflare.com/realtime/realtimekit/ui-kit/api-reference/react-native/rtkplugins/): a panel listing available plugins with controls to activate or deactivate each one.
* [RtkPluginMain](https://developers.cloudflare.com/realtime/realtimekit/ui-kit/api-reference/react-native/rtkpluginmain/): renders the active plugin in a `WebView`.

These components read from `meeting.plugins` automatically once plugins are registered at initialization. If you use `RtkMeeting`, plugin UI is included without any additional setup.

Was this helpful?

YesNo

## On this page

[![](https://developers.cloudflare.com/_astro/logo.te5VL_aD.svg)Docs](https://developers.cloudflare.com/)

```json
{"@context":"https://schema.org","@type":"WebPage","@id":"https://developers.cloudflare.com/realtime/realtimekit/core/plugins/#page","headline":"Plugins · Cloudflare Realtime docs","description":"Register and control plugins in RealtimeKit meetings.","url":"https://developers.cloudflare.com/realtime/realtimekit/core/plugins/","inLanguage":"en","image":"https://developers.cloudflare.com/og-docs.png","dateModified":"2026-07-09","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
```
