INTEGRITY Documentation

UI Kit Addons

A collection of UI Kit addons that extend RealtimeKit's prebuilt UI Kit capabilities with additional interactive components and controls for enhanced meeting experiences.

This page is not available for the iOS, Android, React Nativeplatform.

The UI Kit addons library provides the following categories of components:

Host Controls

Host controls allow meeting hosts to manage participant permissions:

Reactions

Interactive engagement features for participants:

Participant Tile

Customize the participant tile interface:

Participant Tab Actions

Add custom actions to the participants tab:

Video Background

Apply visual effects to participant video:

Control Bar

Customize the meeting control bar:

Installation

npm i @cloudflare/realtimekit-ui-addons
npm i @cloudflare/realtimekit-ui-addons
npm i @cloudflare/realtimekit-ui-addons

Usage

import { useState, useEffect } from "react";
import {
	RealtimeKitProvider,
	useRealtimeKitClient,
} from "@cloudflare/realtimekit-react";
import { RtkMeeting } from "@cloudflare/realtimekit-react-ui";
import { registerAddons, defaultConfig } from "@cloudflare/realtimekit-ui";

// Import addons
import CameraHostControl from "@cloudflare/realtimekit-ui-addons/camera-host-control";
import MicHostControl from "@cloudflare/realtimekit-ui-addons/mic-host-control";
import ChatHostControl from "@cloudflare/realtimekit-ui-addons/chat-host-control";
import HandRaise from "@cloudflare/realtimekit-ui-addons/hand-raise";
import ReactionsManagerAddon from "@cloudflare/realtimekit-ui-addons/reactions-manager";
import ParticipantTileMenu from "@cloudflare/realtimekit-ui-addons/participant-tile-menu";
import ParticipantMenuItem from "@cloudflare/realtimekit-ui-addons/participant-menu-item";
import ParticipantsTabAction from "@cloudflare/realtimekit-ui-addons/participants-tab-action";
import ParticipantsTabToggle from "@cloudflare/realtimekit-ui-addons/participants-tab-toggle";
import RealtimeKitVideoBackground from "@cloudflare/realtimekit-ui-addons/video-background";
import CustomControlbarButton from "@cloudflare/realtimekit-ui-addons/custom-controlbar-button";

function App() {
	const [meeting, initMeeting] = useRealtimeKitClient();
	const [authToken, setAuthToken] = useState("<participant_auth_token>");
	const [config, setConfig] = useState(defaultConfig);

	useEffect(() => {
		if (authToken) {
			initMeeting({
				authToken: authToken,
			});
		}
	}, [authToken]);

	useEffect(() => {
		const initializeAddons = async () => {
			if (!meeting) return;

			// Initialize addons
			const cameraHostControl = await CameraHostControl.init({
				meeting,
				hostPresets: ["webinar_presenter"],
				targetPresets: ["webinar_viewer"],
				addActionInParticipantMenu: true,
			});

			const micHostControl = await MicHostControl.init({
				meeting,
				hostPresets: ["webinar_presenter"],
				targetPresets: ["webinar_viewer"],
				addActionInParticipantMenu: true,
			});

			const chatHostControl = await ChatHostControl.init({
				meeting,
				hostPresets: ["webinar_presenter"],
				targetPresets: ["webinar_viewer"],
				addActionInParticipantMenu: true,
			});

			const handRaise = await HandRaise.init({
				meeting,
				canRaiseHand: true,
				canManageRaisedHand: true,
				handRaiseIcon:
					'<svg fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path d="M4 12.02c0 1.06.2 2.1.6 3.08l.6 1.42c.22.55.64 1.01 1.17 1.29.27.14.56.21.86.21h2.55c.77 0 1.49-.41 1.87-1.08.5-.87 1.02-1.7 1.72-2.43l1.32-1.39c.44-.46.97-.84 1.49-1.23l.59-.45a.6.6 0 0 0 .23-.47c0-.75-.54-1.57-1.22-1.79a3.34 3.34 0 0 0-2.78.29V4.5a1.5 1.5 0 0 0-2.05-1.4 1.5 1.5 0 0 0-2.9 0A1.5 1.5 0 0 0 6 4.5v.09A1.5 1.5 0 0 0 4 6v6.02ZM8 4.5v4a.5.5 0 0 0 1 0v-5a.5.5 0 0 1 1 0v5a.5.5 0 0 0 1 0v-4a.5.5 0 0 1 1 0v6a.5.5 0 0 0 .85.37h.01c.22-.22.44-.44.72-.58.7-.35 2.22-.57 2.4.5l-.53.4c-.52.4-1.04.78-1.48 1.24l-1.33 1.38c-.75.79-1.31 1.7-1.85 2.63-.21.36-.6.58-1.01.58H7.23a.87.87 0 0 1-.4-.1 1.55 1.55 0 0 1-.71-.78l-.59-1.42a7.09 7.09 0 0 1-.53-2.7V6a.5.5 0 0 1 1 0v3.5a.5.5 0 0 0 1 0v-5a.5.5 0 0 1 1 0Z" fill="#ff0000"></path></svg>',
			});

			const CUSTOM_REACTIONS = [
				{ emoji: "🔥", label: "fire" },
				{ emoji: "😢", label: "sad" },
				{ emoji: "👍", label: "thumbs up" },
				{ emoji: "👎", label: "thumbs down" },
				{ emoji: "❤️", label: "heart" },
				{ emoji: "😂", label: "laugh" },
				{ emoji: "👏", label: "clap" },
				{ emoji: "🎉", label: "celebrate" },
			];

			const reactionsAddon = await ReactionsManagerAddon.init({
				meeting,
				reactions: CUSTOM_REACTIONS,
				canSendReactions: true,
			});

			const participantTileMenu = new ParticipantTileMenu(
				[
					{
						label: "Custom Toggle",
						onClick: (participantId) => {
							console.log("Clicked on custom toggle for ", participantId);
						},
					},
				],
				"top-right",
			);

			const rightTickSVG =
				"<svg xmlns='http://www.w3.org/2000/svg' width='24' height='24'><path d='M4 12l6 6 10-14' fill='none' stroke='currentColor' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'/></svg>";

			const participantMenuItem = new ParticipantMenuItem({
				label: "Custom Menu Item",
				icon: rightTickSVG,
				styles: "rtk-icon { color: green !important; }",
				onClick: () => {
					alert("Participant Menu Item clicked");
				},
			});

			const participantsTabAction = new ParticipantsTabAction({
				onClick: () => {
					alert("Clicked!");
				},
				label: "Click me",
				position: "start",
			});

			const participantsTabToggle = new ParticipantsTabToggle({
				onEnabled: () => {
					alert("toggled true!");
				},
				onDisabled: () => {
					alert("toggled false!");
				},
				label: "Click me",
				initialValue: () => true,
				position: "start",
			});

			const videoBackground = await RealtimeKitVideoBackground.init({
				modes: ["blur", "virtual", "random"],
				blurStrength: 30,
				meeting,
				images: [
					"https://images.unsplash.com/photo-1487088678257-3a541e6e3922?q=80&w=2874&auto=format&fit=crop&ixlib=rb-4.0.3",
					"https://images.unsplash.com/photo-1496715976403-7e36dc43f17b?q=80&w=2848&auto=format&fit=crop&ixlib=rb-4.0.3",
					"https://images.unsplash.com/photo-1600431521340-491eca880813?q=80&w=2938&auto=format&fit=crop&ixlib=rb-4.0.3",
				],
				randomCount: 10,
				onVideoBackgroundUpdate: ({ backgroundMode, backgroundURL }) => {
					console.log("videoBackgroundUpdated => ", {
						backgroundMode,
						backgroundURL,
					});
				},
			});

			const customControlBarButton = new CustomControlbarButton({
				position: "left",
				icon: '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none"><path d="M12 17.75a1.25 1.25 0 1 1 0 2.5a1.25 1.25 0 0 1 0-2.5zM12 14c0-2.5 4-2.5 4-6a4 4 0 1 0-8 0" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" /></svg>',
				label: "Click Me!",
				onClick: () => alert("Custom Control Bar Button Clicked"),
			});

			// Register addons
			const newConfig = registerAddons(
				[
					cameraHostControl,
					micHostControl,
					chatHostControl,
					handRaise,
					reactionsAddon,
					participantTileMenu,
					participantMenuItem,
					participantsTabAction,
					participantsTabToggle,
					videoBackground,
					customControlBarButton,
				],
				meeting,
			);

			setConfig(newConfig);
		};

		initializeAddons();
	}, [meeting]);

	return (
		<RealtimeKitProvider value={meeting}>
			<RtkMeeting showSetupScreen={true} meeting={meeting} config={config} />
		</RealtimeKitProvider>
	);
}

Add the UI Kit library to your HTML:

<script type="module">
	import { defineCustomElements } from "https://cdn.jsdelivr.net/npm/@cloudflare/realtimekit-ui@latest/loader/index.es2017.js";
	defineCustomElements();
</script>

Place the rtk-meeting component in your HTML file:

<body>
	<rtk-meeting></rtk-meeting>
</body>

Initialize the meeting and configure addons in your script:

<script type="module">
	import RealtimeKitClient from "https://cdn.jsdelivr.net/npm/@cloudflare/realtimekit@latest/dist/index.es.js";
	import { registerAddons } from "https://cdn.jsdelivr.net/npm/@cloudflare/realtimekit-ui@latest/dist/index.es.js";

	// Import addons
	import CameraHostControl from "https://cdn.jsdelivr.net/npm/@cloudflare/realtimekit-ui-addons@latest/camera-host-control/index.js";
	import MicHostControl from "https://cdn.jsdelivr.net/npm/@cloudflare/realtimekit-ui-addons@latest/mic-host-control/index.js";
	import ChatHostControl from "https://cdn.jsdelivr.net/npm/@cloudflare/realtimekit-ui-addons@latest/chat-host-control/index.js";
	import HandRaise from "https://cdn.jsdelivr.net/npm/@cloudflare/realtimekit-ui-addons@latest/hand-raise/index.js";
	import ReactionsManagerAddon from "https://cdn.jsdelivr.net/npm/@cloudflare/realtimekit-ui-addons@latest/reactions-manager/index.js";
	import ParticipantTileMenu from "https://cdn.jsdelivr.net/npm/@cloudflare/realtimekit-ui-addons@latest/participant-tile-menu/index.js";
	import ParticipantMenuItem from "https://cdn.jsdelivr.net/npm/@cloudflare/realtimekit-ui-addons@latest/participant-menu-item/index.js";
	import ParticipantsTabAction from "https://cdn.jsdelivr.net/npm/@cloudflare/realtimekit-ui-addons@latest/participants-tab-action/index.js";
	import ParticipantsTabToggle from "https://cdn.jsdelivr.net/npm/@cloudflare/realtimekit-ui-addons@latest/participants-tab-toggle/index.js";
	import RealtimeKitVideoBackground from "https://cdn.jsdelivr.net/npm/@cloudflare/realtimekit-ui-addons@latest/video-background/index.js";
	import CustomControlbarButton from "https://cdn.jsdelivr.net/npm/@cloudflare/realtimekit-ui-addons@latest/custom-controlbar-button/index.js";

	// Initialize meeting
	const meeting = await RealtimeKitClient.init({
		authToken: "<participant_auth_token>",
	});

	// Initialize addons
	// Host controls
	const cameraHostControl = await CameraHostControl.init({
		meeting,
		hostPresets: ["webinar_presenter"],
		targetPresets: ["webinar_viewer"],
		addActionInParticipantMenu: true,
	});

	const micHostControl = await MicHostControl.init({
		meeting,
		hostPresets: ["webinar_presenter"],
		targetPresets: ["webinar_viewer"],
		addActionInParticipantMenu: true,
	});

	const chatHostControl = await ChatHostControl.init({
		meeting,
		hostPresets: ["webinar_presenter"],
		targetPresets: ["webinar_viewer"],
		addActionInParticipantMenu: true,
	});

	// Reactions
	const handRaise = await HandRaise.init({
		meeting,
		canRaiseHand: true,
		canManageRaisedHand: true,
		handRaiseIcon:
			'<svg fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path d="M4 12.02c0 1.06.2 2.1.6 3.08l.6 1.42c.22.55.64 1.01 1.17 1.29.27.14.56.21.86.21h2.55c.77 0 1.49-.41 1.87-1.08.5-.87 1.02-1.7 1.72-2.43l1.32-1.39c.44-.46.97-.84 1.49-1.23l.59-.45a.6.6 0 0 0 .23-.47c0-.75-.54-1.57-1.22-1.79a3.34 3.34 0 0 0-2.78.29V4.5a1.5 1.5 0 0 0-2.05-1.4 1.5 1.5 0 0 0-2.9 0A1.5 1.5 0 0 0 6 4.5v.09A1.5 1.5 0 0 0 4 6v6.02ZM8 4.5v4a.5.5 0 0 0 1 0v-5a.5.5 0 0 1 1 0v5a.5.5 0 0 0 1 0v-4a.5.5 0 0 1 1 0v6a.5.5 0 0 0 .85.37h.01c.22-.22.44-.44.72-.58.7-.35 2.22-.57 2.4.5l-.53.4c-.52.4-1.04.78-1.48 1.24l-1.33 1.38c-.75.79-1.31 1.7-1.85 2.63-.21.36-.6.58-1.01.58H7.23a.87.87 0 0 1-.4-.1 1.55 1.55 0 0 1-.71-.78l-.59-1.42a7.09 7.09 0 0 1-.53-2.7V6a.5.5 0 0 1 1 0v3.5a.5.5 0 0 0 1 0v-5a.5.5 0 0 1 1 0Z" fill="#ff0000"></path></svg>',
	});

	const CUSTOM_REACTIONS = [
		{ emoji: "🔥", label: "fire" },
		{ emoji: "😢", label: "sad" },
		{ emoji: "👍", label: "thumbs up" },
		{ emoji: "👎", label: "thumbs down" },
		{ emoji: "❤️", label: "heart" },
		{ emoji: "😂", label: "laugh" },
		{ emoji: "👏", label: "clap" },
		{ emoji: "🎉", label: "celebrate" },
	];

	const reactionsAddon = await ReactionsManagerAddon.init({
		meeting,
		reactions: CUSTOM_REACTIONS,
		canSendReactions: true,
	});

	// Participant Tile
	const participantTileMenu = new ParticipantTileMenu(
		[
			{
				label: "Custom Toggle",
				onClick: (participantId) => {
					console.log("Clicked on custom toggle for ", participantId);
				},
			},
		],
		"top-right",
	);

	// Participant Tab Actions
	const rightTickSVG =
		"<svg xmlns='http://www.w3.org/2000/svg' width='24' height='24'><path d='M4 12l6 6 10-14' fill='none' stroke='currentColor' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'/></svg>";

	const participantMenuItem = new ParticipantMenuItem({
		label: "Custom Menu Item",
		icon: rightTickSVG,
		styles: "rtk-icon { color: green !important; }",
		onClick: () => {
			alert("Participant Menu Item clicked");
		},
	});

	const participantsTabAction = new ParticipantsTabAction({
		onClick: () => {
			alert("Clicked!");
		},
		label: "Click me",
		position: "start",
	});

	const participantsTabToggle = new ParticipantsTabToggle({
		onEnabled: () => {
			alert("toggled true!");
		},
		onDisabled: () => {
			alert("toggled false!");
		},
		label: "Click me",
		initialValue: () => true,
		position: "start",
	});

	// Video Background (Effects)
	const videoBackground = await RealtimeKitVideoBackground.init({
		modes: ["blur", "virtual", "random"],
		blurStrength: 30, // 0 - 100 for opacity
		meeting,
		images: [
			"https://images.unsplash.com/photo-1487088678257-3a541e6e3922?q=80&w=2874&auto=format&fit=crop&ixlib=rb-4.0.3",
			"https://images.unsplash.com/photo-1496715976403-7e36dc43f17b?q=80&w=2848&auto=format&fit=crop&ixlib=rb-4.0.3",
			"https://images.unsplash.com/photo-1600431521340-491eca880813?q=80&w=2938&auto=format&fit=crop&ixlib=rb-4.0.3",
		],
		randomCount: 10,
		onVideoBackgroundUpdate: ({ backgroundMode, backgroundURL }) => {
			console.log("videoBackgroundUpdated => ", {
				backgroundMode,
				backgroundURL,
			});
		},
	});

	// Control Bar
	const customControlBarButton = new CustomControlbarButton({
		position: "left",
		icon: '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none"><path d="M12 17.75a1.25 1.25 0 1 1 0 2.5a1.25 1.25 0 0 1 0-2.5zM12 14c0-2.5 4-2.5 4-6a4 4 0 1 0-8 0" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" /></svg>',
		label: "Click Me!",
		onClick: () => alert("Custom Control Bar Button Clicked"),
	});

	// Register addons
	const newConfig = registerAddons(
		[
			cameraHostControl,
			micHostControl,
			chatHostControl,
			handRaise,
			reactionsAddon,
			participantTileMenu,
			participantMenuItem,
			participantsTabAction,
			participantsTabToggle,
			videoBackground,
			customControlBarButton,
		],
		meeting,
	);

	// Apply config to meeting component
	document.querySelector("rtk-meeting").showSetupScreen = true;
	document.querySelector("rtk-meeting").meeting = meeting;
	document.querySelector("rtk-meeting").config = newConfig;
</script>

In your component template, add the meeting component:

<rtk-meeting [meeting]="meeting" [config]="config"></rtk-meeting>

In your component TypeScript file:

import { Component, OnInit } from "@angular/core";
import RealtimeKitClient from "@cloudflare/realtimekit";
import { registerAddons, defaultConfig } from "@cloudflare/realtimekit-ui";

// Import addons
import CameraHostControl from "@cloudflare/realtimekit-ui-addons/camera-host-control";
import MicHostControl from "@cloudflare/realtimekit-ui-addons/mic-host-control";
import ChatHostControl from "@cloudflare/realtimekit-ui-addons/chat-host-control";
import HandRaise from "@cloudflare/realtimekit-ui-addons/hand-raise";
import ReactionsManagerAddon from "@cloudflare/realtimekit-ui-addons/reactions-manager";
import ParticipantTileMenu from "@cloudflare/realtimekit-ui-addons/participant-tile-menu";
import ParticipantMenuItem from "@cloudflare/realtimekit-ui-addons/participant-menu-item";
import ParticipantsTabAction from "@cloudflare/realtimekit-ui-addons/participants-tab-action";
import ParticipantsTabToggle from "@cloudflare/realtimekit-ui-addons/participants-tab-toggle";
import RealtimeKitVideoBackground from "@cloudflare/realtimekit-ui-addons/video-background";
import CustomControlbarButton from "@cloudflare/realtimekit-ui-addons/custom-controlbar-button";

@Component({
	selector: "app-meeting",
	templateUrl: "./meeting.component.html",
})
export class MeetingComponent implements OnInit {
	meeting: any;
	config: any = defaultConfig;

	async ngOnInit() {
		// Initialize meeting
		this.meeting = await RealtimeKitClient.init({
			authToken: "<participant_auth_token>",
		});

		// Initialize addons
		const cameraHostControl = await CameraHostControl.init({
			meeting: this.meeting,
			hostPresets: ["webinar_presenter"],
			targetPresets: ["webinar_viewer"],
			addActionInParticipantMenu: true,
		});

		const micHostControl = await MicHostControl.init({
			meeting: this.meeting,
			hostPresets: ["webinar_presenter"],
			targetPresets: ["webinar_viewer"],
			addActionInParticipantMenu: true,
		});

		const chatHostControl = await ChatHostControl.init({
			meeting: this.meeting,
			hostPresets: ["webinar_presenter"],
			targetPresets: ["webinar_viewer"],
			addActionInParticipantMenu: true,
		});

		const handRaise = await HandRaise.init({
			meeting: this.meeting,
			canRaiseHand: true,
			canManageRaisedHand: true,
			handRaiseIcon:
				'<svg fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path d="M4 12.02c0 1.06.2 2.1.6 3.08l.6 1.42c.22.55.64 1.01 1.17 1.29.27.14.56.21.86.21h2.55c.77 0 1.49-.41 1.87-1.08.5-.87 1.02-1.7 1.72-2.43l1.32-1.39c.44-.46.97-.84 1.49-1.23l.59-.45a.6.6 0 0 0 .23-.47c0-.75-.54-1.57-1.22-1.79a3.34 3.34 0 0 0-2.78.29V4.5a1.5 1.5 0 0 0-2.05-1.4 1.5 1.5 0 0 0-2.9 0A1.5 1.5 0 0 0 6 4.5v.09A1.5 1.5 0 0 0 4 6v6.02ZM8 4.5v4a.5.5 0 0 0 1 0v-5a.5.5 0 0 1 1 0v5a.5.5 0 0 0 1 0v-4a.5.5 0 0 1 1 0v6a.5.5 0 0 0 .85.37h.01c.22-.22.44-.44.72-.58.7-.35 2.22-.57 2.4.5l-.53.4c-.52.4-1.04.78-1.48 1.24l-1.33 1.38c-.75.79-1.31 1.7-1.85 2.63-.21.36-.6.58-1.01.58H7.23a.87.87 0 0 1-.4-.1 1.55 1.55 0 0 1-.71-.78l-.59-1.42a7.09 7.09 0 0 1-.53-2.7V6a.5.5 0 0 1 1 0v3.5a.5.5 0 0 0 1 0v-5a.5.5 0 0 1 1 0Z" fill="#ff0000"></path></svg>',
		});

		const CUSTOM_REACTIONS = [
			{ emoji: "🔥", label: "fire" },
			{ emoji: "😢", label: "sad" },
			{ emoji: "👍", label: "thumbs up" },
			{ emoji: "👎", label: "thumbs down" },
			{ emoji: "❤️", label: "heart" },
			{ emoji: "😂", label: "laugh" },
			{ emoji: "👏", label: "clap" },
			{ emoji: "🎉", label: "celebrate" },
		];

		const reactionsAddon = await ReactionsManagerAddon.init({
			meeting: this.meeting,
			reactions: CUSTOM_REACTIONS,
			canSendReactions: true,
		});

		const participantTileMenu = new ParticipantTileMenu(
			[
				{
					label: "Custom Toggle",
					onClick: (participantId: string) => {
						console.log("Clicked on custom toggle for ", participantId);
					},
				},
			],
			"top-right",
		);

		const rightTickSVG =
			"<svg xmlns='http://www.w3.org/2000/svg' width='24' height='24'><path d='M4 12l6 6 10-14' fill='none' stroke='currentColor' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'/></svg>";

		const participantMenuItem = new ParticipantMenuItem({
			label: "Custom Menu Item",
			icon: rightTickSVG,
			styles: "rtk-icon { color: green !important; }",
			onClick: () => {
				alert("Participant Menu Item clicked");
			},
		});

		const participantsTabAction = new ParticipantsTabAction({
			onClick: () => {
				alert("Clicked!");
			},
			label: "Click me",
			position: "start",
		});

		const participantsTabToggle = new ParticipantsTabToggle({
			onEnabled: () => {
				alert("toggled true!");
			},
			onDisabled: () => {
				alert("toggled false!");
			},
			label: "Click me",
			initialValue: () => true,
			position: "start",
		});

		const videoBackground = await RealtimeKitVideoBackground.init({
			modes: ["blur", "virtual", "random"],
			blurStrength: 30,
			meeting: this.meeting,
			images: [
				"https://images.unsplash.com/photo-1487088678257-3a541e6e3922?q=80&w=2874&auto=format&fit=crop&ixlib=rb-4.0.3",
				"https://images.unsplash.com/photo-1496715976403-7e36dc43f17b?q=80&w=2848&auto=format&fit=crop&ixlib=rb-4.0.3",
				"https://images.unsplash.com/photo-1600431521340-491eca880813?q=80&w=2938&auto=format&fit=crop&ixlib=rb-4.0.3",
			],
			randomCount: 10,
			onVideoBackgroundUpdate: ({ backgroundMode, backgroundURL }: any) => {
				console.log("videoBackgroundUpdated => ", {
					backgroundMode,
					backgroundURL,
				});
			},
		});

		const customControlBarButton = new CustomControlbarButton({
			position: "left",
			icon: '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none"><path d="M12 17.75a1.25 1.25 0 1 1 0 2.5a1.25 1.25 0 0 1 0-2.5zM12 14c0-2.5 4-2.5 4-6a4 4 0 1 0-8 0" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" /></svg>',
			label: "Click Me!",
			onClick: () => alert("Custom Control Bar Button Clicked"),
		});

		// Register addons
		this.config = registerAddons(
			[
				cameraHostControl,
				micHostControl,
				chatHostControl,
				handRaise,
				reactionsAddon,
				participantTileMenu,
				participantMenuItem,
				participantsTabAction,
				participantsTabToggle,
				videoBackground,
				customControlBarButton,
			],
			this.meeting,
		);
	}
}

Programmatic Control

Some addons support programmatic control for dynamic changes during a meeting.

Video Background

You can apply, replace, or remove video backgrounds programmatically:

// Apply a virtual background
await videoBackground.applyVirtualBackground(
	"https://images.unsplash.com/photo-1600431521340-491eca880813?q=80&w=2938&auto=format&fit=crop&ixlib=rb-4.0.3",
);

// Apply a blur background
await videoBackground.applyBlurBackground();

// Remove background (return to normal video)
await videoBackground.removeBackground();

Some addons support programmatic control for dynamic changes during a meeting.

Video Background

You can apply, replace, or remove video backgrounds programmatically:

// Apply a virtual background
await videoBackground.applyVirtualBackground(
	"https://images.unsplash.com/photo-1600431521340-491eca880813?q=80&w=2938&auto=format&fit=crop&ixlib=rb-4.0.3",
);

// Apply a blur background
await videoBackground.applyBlurBackground();

// Remove background (return to normal video)
await videoBackground.removeBackground();

Some addons support programmatic control for dynamic changes during a meeting.

Video Background

You can apply, replace, or remove video backgrounds programmatically:

// Apply a virtual background
await videoBackground.applyVirtualBackground(
	"https://images.unsplash.com/photo-1600431521340-491eca880813?q=80&w=2938&auto=format&fit=crop&ixlib=rb-4.0.3",
);

// Apply a blur background
await videoBackground.applyBlurBackground();

// Remove background (return to normal video)
await videoBackground.removeBackground();