In this guide, we will learn how to add a custom controlbar for your RealtimeKit meeting experience.
RealtimeKit UI Kit provides the RtkControlbar component for a default controlbar.
If you need additional controls, replace RtkControlbar with individual UI Kit components and custom elements.
Import the required components and React hook:
import { useRef } from "react";
import {
RtkFullscreenToggle,
RtkSettingsToggle,
RtkScreenShareToggle,
RtkMicToggle,
RtkCameraToggle,
RtkStageToggle,
RtkLeaveButton,
RtkMoreToggle,
RtkPipToggle,
RtkMuteAllButton,
RtkBreakoutRoomsToggle,
RtkRecordingToggle,
RtkChatToggle,
RtkPollsToggle,
RtkParticipantsToggle,
RtkPluginsToggle,
} from "@cloudflare/realtimekit-ui";In your RtkUIProvider from Build Your Own UI, replace:
<RtkControlbar />with:
<div
style={{
display: "flex",
width: "100%",
padding: "8px 12px",
color: "white",
justifyContent: "space-between",
}}
>
<div
id="controlbar-left"
style={{ display: "flex", alignItems: "center", justifyContent: "center" }}
>
<RtkFullscreenToggle targetElement={fullScreenRef.current} />
<RtkSettingsToggle />
<RtkScreenShareToggle />
</div>
<div
id="controlbar-center"
style={{ display: "flex", alignItems: "center", justifyContent: "center" }}
>
<RtkMicToggle />
<RtkCameraToggle />
<RtkStageToggle />
<RtkLeaveButton />
<RtkMoreToggle>
<div slot="more-elements">
<RtkPipToggle variant="horizontal" />
<RtkMuteAllButton variant="horizontal" />
<RtkBreakoutRoomsToggle variant="horizontal" />
<RtkRecordingToggle variant="horizontal" />
</div>
</RtkMoreToggle>
</div>
<div
id="controlbar-right"
style={{ display: "flex", alignItems: "center", justifyContent: "center" }}
>
<RtkChatToggle />
<RtkPollsToggle />
<RtkParticipantsToggle />
<RtkPluginsToggle />
</div>
</div>Define a ref for the fullscreen target and attach it to your container element:
const fullScreenRef = useRef<HTMLDivElement>(null);
// In your RtkUIProvider, add the ref to the container
<RtkUIProvider
ref={fullScreenRef}
meeting={meeting}
showSetupScreen={false}
style={{
display: "flex",
flexDirection: "column",
height: "100vh",
margin: 0,
}}
>
{/* Your controlbar and other components */}
</RtkUIProvider>
// Pass the ref's current element to RtkFullscreenToggle
<RtkFullscreenToggle targetElement={fullScreenRef.current} />A complete example to build your own UI with custom controlbar can be found here ↗ with the custom controlbar component here ↗.
RealtimeKit UI Kit provides the rtk-controlbar component for a default controlbar.
If you need additional controls, replace rtk-controlbar with individual UI Kit components and custom elements. In the renderJoinedScreen function from Build Your Own UI, replace:
<rtk-controlbar
style="display: flex; justify-content: space-between;"
></rtk-controlbar>with:
<div
style="display: flex; width: 100%; padding: 8px 12px; color: white; justify-content: space-between;"
>
<div
id="controlbar-left"
style="display: flex; align-items: center; justify-content: center;"
>
<rtk-fullscreen-toggle id="fullscreen-toggle"></rtk-fullscreen-toggle>
<rtk-settings-toggle></rtk-settings-toggle>
<rtk-screen-share-toggle></rtk-screen-share-toggle>
</div>
<div
id="controlbar-center"
style="display: flex; align-items: center; justify-content: center;"
>
<rtk-mic-toggle></rtk-mic-toggle>
<rtk-camera-toggle></rtk-camera-toggle>
<rtk-stage-toggle></rtk-stage-toggle>
<rtk-leave-button></rtk-leave-button>
<rtk-more-toggle>
<div slot="more-elements">
<rtk-pip-toggle variant="horizontal"></rtk-pip-toggle>
<rtk-mute-all-button variant="horizontal"></rtk-mute-all-button>
<rtk-breakout-rooms-toggle
variant="horizontal"
></rtk-breakout-rooms-toggle>
<rtk-recording-toggle variant="horizontal"></rtk-recording-toggle>
</div>
</rtk-more-toggle>
</div>
<div
id="controlbar-right"
style="display: flex; align-items: center; justify-content: center;"
>
<rtk-chat-toggle></rtk-chat-toggle>
<rtk-polls-toggle></rtk-polls-toggle>
<rtk-participants-toggle></rtk-participants-toggle>
<rtk-plugins-toggle></rtk-plugins-toggle>
</div>
</div>Register the fullscreen target after rendering:
const fullscreenToggle = document.querySelector("#fullscreen-toggle");
if (fullscreenToggle) {
const targetElement = document.querySelector("rtk-ui-provider");
if (targetElement) {
fullscreenToggle.targetElement = targetElement;
}
}A complete example to build your own UI with custom controlbar can be found here ↗ with the custom controlbar component here ↗.
RealtimeKit UI Kit provides the rtk-controlbar component for a default controlbar.
If you need additional controls, replace rtk-controlbar with individual UI Kit components and custom elements. Create a custom controlbar component that uses the RealtimeKit angular components directly.
import { Component, AfterViewInit, ElementRef, ViewChild } from "@angular/core";
@Component({
selector: "app-custom-controlbar",
template: `
<div class="custom-controlbar">
<div class="controlbar-left">
<rtk-fullscreen-toggle #fullscreenToggle></rtk-fullscreen-toggle>
<rtk-settings-toggle></rtk-settings-toggle>
<rtk-screen-share-toggle></rtk-screen-share-toggle>
</div>
<div class="controlbar-center">
<rtk-mic-toggle></rtk-mic-toggle>
<rtk-camera-toggle></rtk-camera-toggle>
<rtk-stage-toggle></rtk-stage-toggle>
<rtk-leave-button></rtk-leave-button>
<rtk-more-toggle>
<div slot="more-elements">
<rtk-pip-toggle variant="horizontal"></rtk-pip-toggle>
<rtk-mute-all-button variant="horizontal"></rtk-mute-all-button>
<rtk-breakout-rooms-toggle
variant="horizontal"
></rtk-breakout-rooms-toggle>
<rtk-recording-toggle variant="horizontal"></rtk-recording-toggle>
</div>
</rtk-more-toggle>
</div>
<div class="controlbar-right">
<rtk-chat-toggle></rtk-chat-toggle>
<rtk-polls-toggle></rtk-polls-toggle>
<rtk-participants-toggle></rtk-participants-toggle>
<rtk-plugins-toggle></rtk-plugins-toggle>
</div>
</div>
`,
styles: [
`
.custom-controlbar {
display: flex;
width: 100%;
padding: 8px 12px;
color: white;
justify-content: space-between;
background-color: rgba(0, 0, 0, 0.8);
border-radius: 8px;
}
.controlbar-left,
.controlbar-center,
.controlbar-right {
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
}
.controlbar-center {
flex: 1;
justify-content: center;
}
`,
],
})
export class CustomControlbarComponent implements AfterViewInit {
@ViewChild("fullscreenToggle", { static: true })
fullscreenToggle!: ElementRef;
ngAfterViewInit() {
// Register the fullscreen target after rendering
this.setupFullscreenToggle();
}
private setupFullscreenToggle() {
const fullscreenElement = this.fullscreenToggle?.nativeElement;
if (fullscreenElement) {
const targetElement = document.querySelector("rtk-ui-provider");
if (targetElement) {
fullscreenElement.targetElement = targetElement;
}
}
}
}In your main meeting component template, replace:
<rtk-controlbar
style="display: flex; justify-content: space-between;"
></rtk-controlbar>with:
<app-custom-controlbar></app-custom-controlbar>import {
Component,
ElementRef,
OnInit,
OnDestroy,
ViewChild,
} from "@angular/core";
@Component({
selector: "app-meeting",
template: `
<rtk-meeting #meetingComponent id="meeting-component">
<!-- Other meeting UI components -->
<rtk-grid></rtk-grid>
<rtk-sidebar></rtk-sidebar>
<!-- Custom controlbar replaces rtk-controlbar -->
<app-custom-controlbar></app-custom-controlbar>
</rtk-meeting>
`,
})
export class MeetingComponent implements OnInit, OnDestroy {
@ViewChild("meetingComponent", { static: true }) meetingElement!: ElementRef;
meeting: any;
private authToken = "<participant_auth_token>";
async ngOnInit() {
const RealtimeKitClient = await import(
"https://cdn.jsdelivr.net/npm/@cloudflare/realtimekit@latest/dist/index.es.js"
);
this.meeting = await RealtimeKitClient.default.init({
authToken: this.authToken,
});
const meetingComponent = this.meetingElement.nativeElement;
meetingComponent.showSetupScreen = true;
meetingComponent.meeting = this.meeting;
}
ngOnDestroy() {
// Cleanup logic
}
}Don't forget to declare your custom controlbar component in your Angular module:
import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from "@angular/core";
import { BrowserModule } from "@angular/platform-browser";
import { AppComponent } from "./app.component";
import { MeetingComponent } from "./meeting.component";
import { CustomControlbarComponent } from "./custom-controlbar.component";
@NgModule({
declarations: [AppComponent, MeetingComponent, CustomControlbarComponent],
imports: [BrowserModule],
providers: [],
bootstrap: [AppComponent],
schemas: [CUSTOM_ELEMENTS_SCHEMA], // Required for RTK web components
})
export class AppModule {}You can further customize the controlbar by adding your own buttons or modifying the layout:
import { Component, AfterViewInit, ElementRef, ViewChild } from "@angular/core";
@Component({
selector: "app-enhanced-controlbar",
template: `
<div class="custom-controlbar">
<div class="controlbar-left">
<rtk-fullscreen-toggle #fullscreenToggle></rtk-fullscreen-toggle>
<rtk-settings-toggle></rtk-settings-toggle>
<button class="custom-button" (click)="onCustomAction()">
Custom Action
</button>
</div>
<div class="controlbar-center">
<rtk-mic-toggle></rtk-mic-toggle>
<rtk-camera-toggle></rtk-camera-toggle>
<rtk-stage-toggle></rtk-stage-toggle>
<rtk-leave-button></rtk-leave-button>
</div>
<div class="controlbar-right">
<rtk-chat-toggle></rtk-chat-toggle>
<rtk-participants-toggle></rtk-participants-toggle>
</div>
</div>
`,
styles: [
`
.custom-controlbar {
display: flex;
width: 100%;
padding: 8px 12px;
color: white;
justify-content: space-between;
background-color: rgba(0, 0, 0, 0.8);
border-radius: 8px;
}
.controlbar-left,
.controlbar-center,
.controlbar-right {
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
}
.custom-button {
background: #0051c3;
border: none;
color: white;
padding: 8px 12px;
border-radius: 4px;
cursor: pointer;
font-size: 12px;
}
.custom-button:hover {
background: #003d99;
}
`,
],
})
export class EnhancedControlbarComponent implements AfterViewInit {
@ViewChild("fullscreenToggle", { static: true })
fullscreenToggle!: ElementRef;
ngAfterViewInit() {
this.setupFullscreenToggle();
}
private setupFullscreenToggle() {
const fullscreenElement = this.fullscreenToggle?.nativeElement;
if (fullscreenElement) {
const targetElement = document.querySelector("rtk-ui-provider");
if (targetElement) {
fullscreenElement.targetElement = targetElement;
}
}
}
onCustomAction() {
console.log("Custom action triggered");
// Add your custom logic here
}
}This approach gives you complete control over the controlbar layout while maintaining Angular's component architecture and leveraging RealtimeKit's built-in functionality.
The iOS UI Kit provides RtkMeetingControlBar as the default controlbar for group call meetings. To build a custom controlbar, compose individual button components inside a RtkTabBar.
| Component | Description |
|---|---|
RtkAudioButtonControlBar |
Microphone toggle with automatic state management |
RtkVideoButtonControlBar |
Camera toggle with automatic state management |
RtkEndMeetingControlBarButton |
Leave/end meeting button with confirmation dialog |
RtkMoreButtonControlBar |
"More" menu button with bottom sheet |
RtkSwitchCameraButtonControlBar |
Front/back camera switch |
RtkStageActionButtonControlBar |
Stage join/leave for webinar and livestream |
RtkControlBarButton |
Base button class for custom buttons |
RtkControlBarSpacerButton |
Invisible spacer for layout |
Create a RtkTabBar and add individual button components:
import RealtimeKitUI
import RealtimeKit
func buildCustomControlBar(
meeting: RealtimeKitClient,
viewController: UIViewController
) -> RtkTabBar {
let tabBar = RtkTabBar(delegate: nil)
let micButton = RtkAudioButtonControlBar(meeting: meeting)
let videoButton = RtkVideoButtonControlBar(rtkClient: meeting)
let switchCameraButton = RtkSwitchCameraButtonControlBar(meeting: meeting)
let endCallButton = RtkEndMeetingControlBarButton(
meeting: meeting,
alertViewController: viewController
)
tabBar.setButtons([micButton, videoButton, switchCameraButton, endCallButton])
return tabBar
}override func viewDidLoad() {
super.viewDidLoad()
let controlBar = buildCustomControlBar(
meeting: meeting,
viewController: self
)
controlBar.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(controlBar)
NSLayoutConstraint.activate([
controlBar.bottomAnchor.constraint(
equalTo: view.safeAreaLayoutGuide.bottomAnchor
),
controlBar.leadingAnchor.constraint(equalTo: view.leadingAnchor),
controlBar.trailingAnchor.constraint(equalTo: view.trailingAnchor),
controlBar.heightAnchor.constraint(equalToConstant: 80),
])
}Implement RtkMeetingControlBarDataSource to replace specific buttons while keeping the default controlbar layout:
class CustomControlBarDataSource: RtkMeetingControlBarDataSource {
func getMicControlBarButton(
for meeting: RealtimeKitClient
) -> RtkControlBarButton? {
// Return a custom mic button, or nil to use the default
let button = RtkAudioButtonControlBar(meeting: meeting)
return button
}
func getVideoControlBarButton(
for meeting: RealtimeKitClient
) -> RtkControlBarButton? {
// Return a custom video button, or nil to use the default
let button = RtkVideoButtonControlBar(rtkClient: meeting)
return button
}
}
// Assign the data source
let controlBar = RtkMeetingControlBar(
meeting: meeting,
delegate: nil,
presentingViewController: self
)
controlBar.dataSource = CustomControlBarDataSource()Create a custom button using RtkControlBarButton:
let customButton = RtkControlBarButton(
image: RtkImage(image: UIImage(systemName: "hand.raised.fill")),
title: "Raise Hand"
)
customButton.addTarget(self, action: #selector(onRaiseHand), for: .touchUpInside)
// Add it alongside other buttons
tabBar.setButtons([micButton, videoButton, customButton, endCallButton])The Android UI Kit provides RtkMeetingControlBarView as the default controlbar. To build a custom controlbar, use individual button components in an XML layout and activate them with the meeting object.
| Component | Activate with | Description |
|---|---|---|
RtkMicToggleButton |
RealtimeKitClient |
Microphone toggle with state management |
RtkCameraToggleButton |
RealtimeKitClient |
Camera toggle with state management |
RtkLeaveButton |
RealtimeKitClient |
Leave/end meeting button with confirmation dialog |
RtkMoreToggleButton |
RealtimeKitClient |
"More" menu button |
RtkControlBarButton |
— | Base button class for custom buttons |
<?xml version="1.0" encoding="utf-8"?>
<com.cloudflare.realtimekit.ui.view.controlbars.RtkControlBarView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/customControlBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:padding="8dp">
<com.cloudflare.realtimekit.ui.view.controlbarbuttons.RtkMicToggleButton
android:id="@+id/micToggle"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_margin="4dp" />
<com.cloudflare.realtimekit.ui.view.controlbarbuttons.RtkCameraToggleButton
android:id="@+id/cameraToggle"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_margin="4dp" />
<com.cloudflare.realtimekit.ui.view.controlbarbuttons.RtkMoreToggleButton
android:id="@+id/moreToggle"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_margin="4dp" />
<com.cloudflare.realtimekit.ui.view.controlbarbuttons.RtkLeaveButton
android:id="@+id/leaveButton"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_margin="4dp" />
</com.cloudflare.realtimekit.ui.view.controlbars.RtkControlBarView>import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.cloudflare.realtimekit.RealtimeKitClient
import com.cloudflare.realtimekit.ui.view.controlbarbuttons.RtkMicToggleButton
import com.cloudflare.realtimekit.ui.view.controlbarbuttons.RtkCameraToggleButton
import com.cloudflare.realtimekit.ui.view.controlbarbuttons.RtkMoreToggleButton
import com.cloudflare.realtimekit.ui.view.controlbarbuttons.RtkLeaveButton
class CustomMeetingActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_custom_meeting)
// Your meeting initialization code...
}
fun activateControlBar(meeting: RealtimeKitClient) {
findViewById<RtkMicToggleButton>(R.id.micToggle).activate(meeting)
findViewById<RtkCameraToggleButton>(R.id.cameraToggle).activate(meeting)
findViewById<RtkMoreToggleButton>(R.id.moreToggle).activate(meeting)
findViewById<RtkLeaveButton>(R.id.leaveButton).activate(meeting)
}
}Create a custom button in XML:
<com.cloudflare.realtimekit.ui.view.controlbarbuttons.RtkControlBarButton
android:id="@+id/raiseHandButton"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_margin="4dp"
android:text="Raise Hand"
app:rtk_cbb_icon="@drawable/ic_raise_hand"
app:rtk_cbb_variant="BUTTON"
app:rtk_cbb_showText="true" />Handle the click in Kotlin:
val raiseHandButton = findViewById<RtkControlBarButton>(R.id.raiseHandButton)
raiseHandButton.setOnClickListener {
// Custom raise hand logic
}The Flutter UI Kit provides individual toggle button widgets that you can compose into a custom controlbar using standard Flutter layout widgets.
| Widget | Required parameter | Description |
|---|---|---|
RtkSelfAudioToggleButton |
RealtimekitClient |
Microphone toggle with permission handling |
RtkSelfVideoToggleButton |
RealtimekitClient |
Camera toggle with permission handling |
RtkLeaveButton |
RealtimekitClient |
Leave meeting button with confirmation dialog |
Wrap the button widgets in a Row or any custom layout:
import 'package:flutter/material.dart';
import 'package:realtimekit_ui/realtimekit_ui.dart';
class CustomControlBar extends StatelessWidget {
final RealtimekitClient meeting;
const CustomControlBar({required this.meeting, super.key});
@override
Widget build(BuildContext context) {
return Container(
color: Colors.black,
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
RtkSelfAudioToggleButton(meeting: meeting),
RtkSelfVideoToggleButton(meeting: meeting),
RtkLeaveButton(meeting: meeting),
],
),
);
}
}Replace the default controlbar in your Scaffold:
import 'package:flutter/material.dart';
import 'package:realtimekit_ui/realtimekit_ui.dart';
class CustomMeetingScreen extends StatelessWidget {
final RealtimekitClient meeting;
const CustomMeetingScreen({required this.meeting, super.key});
@override
Widget build(BuildContext context) {
return RtkProvider(
meeting: meeting,
uiKitInfo: uiKitInfo,
child: Scaffold(
backgroundColor: Colors.black,
body: Column(
children: [
RtkMeetingTitle(meeting: meeting),
// Your participant grid here
const Spacer(),
],
),
bottomNavigationBar: CustomControlBar(meeting: meeting),
),
);
}
}Add your own IconButton or any Flutter widget next to the RealtimeKit buttons:
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
RtkSelfAudioToggleButton(meeting: meeting),
RtkSelfVideoToggleButton(meeting: meeting),
// Custom raise hand button
IconButton(
onPressed: () {
// Custom raise hand logic
},
icon: const Icon(Icons.back_hand, color: Colors.white),
),
RtkLeaveButton(meeting: meeting),
],
)Pass individualDesignToken to style buttons individually:
RtkSelfAudioToggleButton(
meeting: meeting,
iconSize: 28.0,
iconColor: Colors.cyan,
showLabel: true,
)The React Native UI Kit provides individual toggle components that you can compose into a custom controlbar.
| Component | Required prop | Description |
|---|---|---|
RtkMicToggle |
meeting |
Microphone toggle with permission handling |
RtkCameraToggle |
meeting |
Camera toggle with permission handling |
RtkLeaveButton |
— | Leave/end meeting button with confirmation dialog |
RtkMoreToggle |
meeting |
"More" menu toggle with notification badge |
RtkScreenShareToggle |
meeting |
Screen share toggle |
RtkControlbarButton |
— | Base button for custom actions |
Replace RtkControlbar with individual components in a View:
import React from "react";
import { View, StyleSheet } from "react-native";
import {
RtkMicToggle,
RtkCameraToggle,
RtkLeaveButton,
RtkMoreToggle,
RtkScreenShareToggle,
} from "@cloudflare/realtimekit-react-native-ui";
function CustomControlbar({ meeting }) {
return (
<View style={styles.controlbar}>
<RtkMicToggle meeting={meeting} variant="horizontal" />
<RtkCameraToggle meeting={meeting} variant="horizontal" />
<RtkScreenShareToggle meeting={meeting} variant="horizontal" />
<RtkMoreToggle meeting={meeting} variant="horizontal" />
<RtkLeaveButton />
</View>
);
}
const styles = StyleSheet.create({
controlbar: {
flexDirection: "row",
justifyContent: "space-evenly",
alignItems: "center",
backgroundColor: "#1A1A1A",
paddingVertical: 8,
paddingHorizontal: 16,
},
});In your MeetingScreens component from Build Your Own UI, replace:
<RtkControlbar meeting={meeting} />with:
<CustomControlbar meeting={meeting} />Use RtkControlbarButton to create buttons that match the RealtimeKit design:
import { RtkControlbarButton } from "@cloudflare/realtimekit-react-native-ui";
function CustomControlbar({ meeting }) {
return (
<View style={styles.controlbar}>
<RtkMicToggle meeting={meeting} variant="horizontal" />
<RtkCameraToggle meeting={meeting} variant="horizontal" />
<RtkControlbarButton
label="Raise Hand"
icon={raiseHandSvg}
onClick={() => {
// Custom raise hand logic
}}
variant="horizontal"
/>
<RtkLeaveButton />
</View>
);
}