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.

Prerequisites

This page builds upon the Initialize SDK and Render Default Meeting UI & UI Kit States guides. Make sure you've read those first.

The code examples on this page assume you've already imported the necessary packages and initialized the SDK. We won't repeat those setup steps here for brevity.

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 RtkMeeting, RtkUiProvider, another ui-kit component that acts as a provider, also listens to states and syncs them with the UI Kit components.

Unlike RtkMeeting, RtkUiProvider allows you to pass any child components to it. If any one of the child components is a RealtimeKit component starting with Rtk, RtkUiProvider will coordinate with the RealtimeKit component to sync the states.

Example Code

import {
RealtimeKitProvider,
useRealtimeKitClient,
} from "@cloudflare/realtimekit-react";
import {
RtkUiProvider,
RtkHeader,
RtkStage,
RtkGrid,
RtkSidebar,
RtkControlbar,
RtkNotifications,
RtkParticipantsAudio,
RtkDialogManager,
RtkSetupScreen,
RtkWaitingScreen,
RtkEndedScreen,
States,
} from "@cloudflare/realtimekit-react-ui";
import { useEffect, useState } from "react";
function MeetingContainer() {
const [meeting, initMeeting] = useRealtimeKitClient();
const [currentState, setCurrentState] = useState("idle");
const [showSidebar, setShowSidebar] = useState(false);
useEffect(() => {
initMeeting({
authToken: "participant_auth_token",
});
}, []);
const renderSetupScreen = () => {
return <RtkSetupScreen />;
};
const renderWaitingScreen = () => {
return <RtkWaitingScreen />;
};
const renderJoinedScreen = () => {
return (
<>
<RtkHeader
style={{ display: "flex", justifyContent: "space-between" }}
/>
<RtkStage style={{ flex: 1, flexGrow: 1, flexShrink: 1 }}>
<RtkGrid />
<RtkSidebar
style={{
position: "fixed",
top: "0px",
display: showSidebar ? "block" : "none",
}}
/>
</RtkStage>
<RtkControlbar
style={{ display: "flex", justifyContent: "space-between" }}
/>
</>
);
};
const renderEndedScreen = () => {
return <RtkEndedScreen />;
};
// Listen for state updates from RtkUiProvider
const handleStatesUpdate = (event: { detail: States }) => {
const meetingState = event.detail.meeting;
const states = event.detail;
// Store states to update your custom UI
if (meetingState === "idle" && currentState !== "idle") {
setCurrentState("idle");
} else if (meetingState === "setup" && currentState !== "setup") {
setCurrentState("setup");
} else if (meetingState === "waiting" && currentState !== "waiting") {
setCurrentState("waiting");
} else if (meetingState === "joined" && currentState !== "joined") {
setCurrentState("joined");
} else if (meetingState === "ended" && currentState !== "ended") {
setCurrentState("ended");
}
// Update sidebar visibility based on state
if (states.activeSidebar !== undefined) {
setShowSidebar(states.activeSidebar);
}
};
return (
<RealtimeKitProvider value={meeting}>
<RtkUiProvider
meeting={meeting}
showSetupScreen={true}
onRtkStatesUpdate={handleStatesUpdate}
style={{
display: "flex",
flexDirection: "column",
height: "100vh",
margin: 0,
}}
>
<div
id="meeting-container"
style={{
display: "flex",
flexDirection: "column",
flex: 1,
flexGrow: 1,
flexShrink: 1,
}}
>
{currentState === "idle" && <div>Meeting is loading...</div>}
{currentState === "setup" && renderSetupScreen()}
{currentState === "waiting" && renderWaitingScreen()}
{currentState === "joined" && renderJoinedScreen()}
{currentState === "ended" && renderEndedScreen()}
</div>
<RtkParticipantsAudio />
<RtkDialogManager />
<RtkNotifications />
</RtkUiProvider>
</RealtimeKitProvider>
);
}
function App() {
return <MeetingContainer />;
}

First level split of RtkMeeting using RtkUiProvider has the following components:

RtkHeader is the header component that shows the session name and the session controls.
RtkStage is the container component that contains the grid and sidebar components.
RtkGrid is the grid component that shows the participants in the session.
RtkSidebar is the sidebar component that shows the sidebar, in which chat, polls content shows up.
RtkControlbar is the controlbar component that shows the controls, such as camera, microphone, etc.
RtkNotifications is the notifications component that shows the notifications for the session.
RtkParticipantsAudio is the audio component that helps you listen other participants in the session.
RtkDialogManager is the dialog-manager component that shows the dialogs for the session.

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.