Skip to content
Cloudflare Docs

Build Your Own UI

This guide explains how to use Cloudflare RealtimeKit SDKs to build fully custom real-time video UIs.

Code Examples

If you prefer to learn by seeing examples, please check out the respective example repositories.

Web Examples

Building Your Own UI, With UI Kit

If default meeting component is not enough, and you need more control over layout or behavior, use UI Kit components to build a custom interface. The UI Kit provides pre-built components that sit on top of the Core SDK, letting you mix and match pieces while saving time compared to building from scratch.

Building a custom UI requires managing participant audio, notifications, dialogs, component layout, and screen transitions yourself.

Similar to rtk-meeting, rtk-ui-provider, another ui-kit component that acts as a provider, also listens to states and syncs them with the UI Kit components.

Unlike rtk-meeting, rtk-ui-provider allows you to pass any child components to it, if any one of the child components is a RealtimeKit component starting with rtk-, rtk-ui-provider will coordinate with the RealtimeKit component to sync the states.

Example Code

<!DOCTYPE html>
<html>
<head>
<script type="module">
import { defineCustomElements } from "https://cdn.jsdelivr.net/npm/@cloudflare/realtimekit-ui@latest/loader/index.es2017.js";
defineCustomElements();
</script>
<script src="https://cdn.jsdelivr.net/npm/@cloudflare/realtimekit@latest/dist/browser.js"></script>
</head>
<body style="margin: 0;">
<rtk-ui-provider
id="rtk-ui-provider"
style="display: flex; flex-direction: column; height: 100vh; margin: 0;"
>
<div
id="meeting-container"
style="display: flex; flex-direction: column; flex: 1; flex-grow: 1; flex-shrink: 1;"
>
Meeting will render here...
</div>
<rtk-participants-audio></rtk-participants-audio>
<rtk-dialog-manager></rtk-dialog-manager>
<rtk-notifications></rtk-notifications>
</rtk-ui-provider>
<script type="module">
async function initializeMeeting() {
let currentState = "idle";
const meeting = await RealtimeKitClient.init({
authToken: "participant_auth_token",
});
function renderSetupScreen() {
document.querySelector("#meeting-container").innerHTML = `
<rtk-setup-screen></rtk-setup-screen>
`;
}
function renderWaitingScreen() {
document.querySelector("#meeting-container").innerHTML = `
<rtk-waiting-screen></rtk-waiting-screen>
`;
}
function renderJoinedScreen() {
document.querySelector("#meeting-container").innerHTML = `
<rtk-header style="display: flex; justify-content: space-between;"></rtk-header>
<rtk-stage style="flex: 1; flex-grow: 1; flex-shrink: 1;">
<rtk-grid></rtk-grid>
<rtk-sidebar style="position: fixed; top:0px;"></rtk-sidebar>
</rtk-stage>
<rtk-controlbar style="display: flex; justify-content: space-between;"></rtk-controlbar>
`;
}
function renderEndedScreen() {
document.querySelector("#meeting-container").innerHTML = `
<rtk-ended-screen></rtk-ended-screen>
`;
}
// Listen for state updates from rtk-ui-provider
document
.querySelector("rtk-ui-provider")
.addEventListener("rtkStatesUpdate", (event) => {
// Store states to update your custom UI
const states = event.detail;
if (states.meeting === "idle" && currentState !== "idle") {
currentState = "idle";
document
.querySelector("rtk-ui-provider")
.querySelector("#meeting-container").innerHTML =
"Meeting is loading...";
} else if (states.meeting === "setup" && currentState !== "setup") {
currentState = "setup";
renderSetupScreen();
} else if (
states.meeting === "waiting" &&
currentState !== "waiting"
) {
currentState = "waiting";
renderWaitingScreen();
} else if (
states.meeting === "joined" &&
currentState !== "joined"
) {
currentState = "joined";
renderJoinedScreen();
} else if (states.meeting === "ended" && currentState !== "ended") {
currentState = "ended";
renderEndedScreen();
}
const sidebarComponent = document
.querySelector("rtk-ui-provider")
.querySelector("#meeting-container")
.querySelector("rtk-sidebar");
if (sidebarComponent) {
if (states.activeSidebar) {
sidebarComponent.style.display = "block";
} else {
sidebarComponent.style.display = "none";
}
}
});
document.querySelector("rtk-ui-provider").showSetupScreen = true;
document.querySelector("rtk-ui-provider").meeting = meeting;
}
initializeMeeting();
</script>
</body>
</html>

First level split of rtk-meeting using rtk-ui-provider has the following components:

rtk-header is the header component that shows the session name and the session controls.
rtk-stage is the container component that contains the grid and sidebar components.
rtk-grid is the grid component that shows the participants in the session.
rtk-sidebar is the sidebar component that shows the sidebar, in which chat, polls content shows up.
rtk-controlbar is the controlbar component that shows the controls, such as camera, microphone, etc.
rtk-notifications is the notifications component that shows the notifications for the session.
rtk-participants-audio is the audio component that helps you listen other participants in the session.
rtk-dialog-manager is the dialog-manager component that shows the all supported dialogs, such as settings, breakout rooms, etc.

You can split all of these components further. To see more such components, please refer to our components library.

We have our UI Kit open source on GitHub, you can find it here.