State Management

Last updated Oct 19, 2026

Prerequisites

This page builds upon the Basic Implementation Guide. 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.

WebMobile

ReactWeb ComponentsAngular

How UI Kit Components Communicate

The UI Kit components are able to understand and synchronize with each other because they are nested under the RtkMeeting component. The RtkMeeting component acts as the central coordinator that ensures all components under it stay in sync when it comes to meeting state, participant updates, and other real-time changes.

The UI Kit components are able to understand and synchronize with each other because they are nested under the rtk-meeting component. The rtk-meeting component acts as the central coordinator that ensures all components under it stay in sync when it comes to meeting state, participant updates, and other real-time changes.

The UI Kit components are able to understand and synchronize with each other because they are nested under the rtk-meeting component. The rtk-meeting component acts as the central coordinator that ensures all components under it stay in sync when it comes to meeting state, participant updates, and other real-time changes.

The Android UI Kit manages component communication internally. When you build the UI Kit using RealtimeKitUIBuilder, it creates and coordinates all the necessary UI components. To observe meeting state changes from your application, attach event listeners to the Core SDK's meeting object.

The iOS UI Kit manages component communication internally through the RealtimeKitUI module. To observe meeting state changes from your application, implement event listener protocols and register them on the Core SDK's meeting object.

The React Native UI Kit components communicate and synchronize with each other because they are nested under the RtkMeeting component, wrapped in RealtimeKitProvider and RtkUIProvider. To observe state changes, use hooks from the Core SDK such as useRealtimeKitSelector.

Here's an example of how state synchronization works when opening the participants sidebar:

flowchart LR
    accTitle: Sidebar State Synchronization Example
    accDescr: Example showing how clicking participants toggle updates sidebar through meeting coordination

Toggle["👤 ParticipantsToggle<br/>(User clicks)"]
    Meeting["Meeting Component<br/>(State Coordinator)"]
    Sidebar["Sidebar<br/>(Opens/Closes)"]
    App["Your App<br/>(Gets notified)"]

Toggle -->|"emits rtkStateUpdate<br/>{activeSidebar: true,<br/>sidebar: 'participants'}"|Meeting
    Meeting -->|"propagates state"|Sidebar
    Meeting -->|"emits rtkStatesUpdate"|App

style Meeting fill:#F48120,stroke:#333,stroke-width:2px,color:#fff
    style App fill:#0051C3,stroke:#333,stroke-width:2px,color:#fff

State Flow

  1. Child components emit state updates: When any UI component needs to update state, it emits a rtkStateUpdate event
  2. Meeting component listens and coordinates: The meeting component listens to all these state update events from its children
  3. State propagation: The meeting component propagates the updated state to all other child components to keep them synchronized
  4. External notification: The meeting component also emits a rtkStatesUpdate event that your application can listen to for updating your custom UI or performing actions based on state changes

  5. UI Kit manages internal state: The UI Kit handles all component communication and state synchronization internally

  6. Your app registers event listeners: You attach event listeners (such as RtkMeetingRoomEventListener and RtkSelfEventListener) to the Core SDK's meeting object
  7. Callbacks fire on state changes: When the meeting state changes (for example, a participant joins or audio is toggled), the corresponding listener callback is invoked
  8. You update your UI: Use the callback data to update your application's UI or trigger other actions

  9. UI Kit manages internal state: The RtkMeeting component handles all internal component communication and state synchronization

  10. Your app observes state via hooks: Use useRealtimeKitSelector to select specific meeting properties and re-render when they change
  11. React re-renders on changes: When the selected value changes, React automatically re-renders the component with the new state
  12. You update your UI: Use the observed state values to conditionally render UI elements or trigger side effects

Listening to State Updates

To build custom UI or perform actions based on meeting state changes, you need to observe state updates from the UI Kit.

Listen to the rtkStatesUpdate event emitted by the meeting component. This event provides you with the current state of the UI Kit, including sidebar state, screen sharing status, view type, and more.

Note

Store the states in a state management solution (like React's useState or a plain JavaScript object) to alter your UI based on meeting state changes.

Attach event listeners to the Core SDK's meeting object to observe meeting state changes. The mobile UI Kit handles its own internal state, and your app interacts with the underlying meeting object directly.

Note

Use your platform state management, such as LiveData or StateFlow on Android and @Published properties on iOS, to propagate state changes to your UI.

Use the useRealtimeKitSelector hook from @cloudflare/realtimekit-react-native to observe specific properties on the meeting object. This hook re-renders your component whenever the selected value changes, similar to how selectors work in state management libraries.

Note

Store the states using React's useState to alter your UI based on meeting state changes.

Example Code

For React, you can use the onRtkStatesUpdate prop on the RtkMeeting component to listen for state updates.

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

function App() {
    const [meeting, initMeeting] = useRealtimeKitClient();
    const [authToken, setAuthToken] = useState("<participant_auth_token>");
    const [states, setStates] = useState({});

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

return (
        <RealtimeKitProvider value={meeting}>
            <RtkMeeting
                showSetupScreen={true}
                meeting={meeting}
                onRtkStatesUpdate={(e) => {
                    // Update states when rtk-meeting emits state updates
                    setStates(e.detail);

// Example: Access various state properties
                    console.log("Meeting state:", e.detail.meeting); // 'idle', 'setup', 'joined', 'ended', 'waiting'
                    console.log("Is sidebar active:", e.detail.activeSidebar);
                    console.log("Current sidebar section:", e.detail.sidebar);
                    console.log("Is screen sharing:", e.detail.activeScreenShare);
                }}
            />

{/* Use states to build custom UI */}
            <div className="custom-ui">
                <p>Meeting State: {states.meeting}</p>
                <p>Sidebar Open: {states.activeSidebar ? "Yes" : "No"}</p>
            </div>
        </RealtimeKitProvider>
    );
}

Alternative: Using Refs (Multiple Meetings)

If you're building an experience with multiple meetings on the same page or back-to-back meetings, using refs is recommended to avoid state conflicts between different meeting instances:

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

function App() {
    const [meeting, initMeeting] = useRealtimeKitClient();
    const [authToken, setAuthToken] = useState("<participant_auth_token>");
    const [states, setStates] = useState({});
    const meetingRef = useRef(null);

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

useEffect(() => {
        if (!meetingRef.current) return;

const handleStatesUpdate = (e) => {
            setStates(e.detail);
            console.log("Meeting state:", e.detail.meeting);
            console.log("Is sidebar active:", e.detail.activeSidebar);
        };

// Add event listener via ref
        meetingRef.current.addEventListener("rtkStatesUpdate", handleStatesUpdate);

// Cleanup listener when component unmounts or meeting changes
        return () => {
            meetingRef.current?.removeEventListener(
                "rtkStatesUpdate",
                handleStatesUpdate,
            );
        };
    }, [meetingRef.current]);

return (
        <RealtimeKitProvider value={meeting}>
            <RtkMeeting ref={meetingRef} showSetupScreen={true} meeting={meeting} />

{/* Use states to build custom UI */}
            <div className="custom-ui">
                <p>Meeting State: {states.meeting}</p>
                <p>Sidebar Open: {states.activeSidebar ? "Yes" : "No"}</p>
            </div>
        </RealtimeKitProvider>
    );
}

Note

Using refs with event listeners provides better control and isolation when handling multiple RtkMeeting instances. This approach ensures that state updates from one meeting don't interfere with another, which is crucial for back-to-back meetings or multi-meeting interfaces.

For Web Components, you need to add an event listener to the rtk-meeting component to listen for rtkStatesUpdate events.

<body>
    <rtk-meeting id="meeting-component"></rtk-meeting>
</body>
<script type="module">
    import RealtimeKitClient from "https://cdn.jsdelivr.net/npm/@cloudflare/realtimekit@latest/dist/index.es.js";

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

// Add <rtk-meeting id="meeting-component" /> to your HTML, otherwise you will get error
    const meetingComponent = document.querySelector("#meeting-component");

// Listen for state updates from rtk-meeting
    meetingComponent.addEventListener("rtkStatesUpdate", (event) => {
        console.log("RTK states updated:", event.detail);

// Store states to update your custom UI
        const states = event.detail;

// Example: Access various state properties
        console.log("Meeting state:", states.meeting); // 'idle', 'setup', 'joined', 'ended', 'waiting'
        console.log("Is sidebar active:", states.activeSidebar);
        console.log("Current sidebar section:", states.sidebar); // 'chat', 'participants', 'polls', etc.
        console.log("Is screen sharing:", states.activeScreenShare);

// Update your custom UI based on states
        // For example: Show/hide elements based on meeting state
        if (states.meeting === "joined") {
            // Show meeting controls
        }
    });

meetingComponent.showSetupScreen = true;
    meetingComponent.meeting = meeting;
</script>

For Angular, you need to add an event listener to the rtk-meeting component to listen for rtkStatesUpdate events.

meeting.component.tstypescript

import {
    Component,
    ElementRef,
    OnInit,
    OnDestroy,
    ViewChild,
} from "@angular/core";

@Component({
    selector: "app-meeting",
    template: `
        <rtk-meeting #meetingComponent id="meeting-component"></rtk-meeting>

<!-- Use states to build custom UI -->
        <div class="custom-ui" *ngIf="states">
            <p>Meeting State: {{ states.meeting }}</p>
            <p>Sidebar Open: {{ states.activeSidebar ? "Yes" : "No" }}</p>
            <div *ngIf="states.meeting === 'joined'" class="meeting-controls">
                <!-- Show meeting controls when joined -->
                <p>Meeting controls would go here</p>
            </div>
        </div>
    `,
    styleUrls: ["./meeting.component.css"],
})
export class MeetingComponent implements OnInit, OnDestroy {
    @ViewChild("meetingComponent", { static: true }) meetingElement!: ElementRef;

meeting: any;
    states: any = {};
    private authToken = "<participant_auth_token>";
    private stateUpdateListener?: (event: any) => void;

async ngOnInit() {
        // Import RealtimeKit client dynamically
        const RealtimeKitClient = await import(
            "https://cdn.jsdelivr.net/npm/@cloudflare/realtimekit@latest/dist/index.es.js"
        );

// Initialize the meeting
        this.meeting = await RealtimeKitClient.default.init({
            authToken: this.authToken,
        });

// Set up the meeting component
        const meetingComponent = this.meetingElement.nativeElement;

// Create the event listener
        this.stateUpdateListener = (event: any) => {
            console.log("RTK states updated:", event.detail);

// Store states to update your custom UI
            this.states = event.detail;

// Example: Access various state properties
            console.log("Meeting state:", this.states.meeting); // 'idle', 'setup', 'joined', 'ended', 'waiting'
            console.log("Is sidebar active:", this.states.activeSidebar);
            console.log("Current sidebar section:", this.states.sidebar); // 'chat', 'participants', 'polls', etc.
            console.log("Is screen sharing:", this.states.activeScreenShare);

// Update your custom UI based on states
            // For example: Show/hide elements based on meeting state
            if (this.states.meeting === "joined") {
                // Show meeting controls
                console.log("Meeting joined - showing controls");
            }
        };

// Listen for state updates from rtk-meeting
        meetingComponent.addEventListener(
            "rtkStatesUpdate",
            this.stateUpdateListener,
        );

// Configure the meeting component
        meetingComponent.showSetupScreen = true;
        meetingComponent.meeting = this.meeting;
    }

ngOnDestroy() {
        // Clean up event listener when component is destroyed
        if (this.stateUpdateListener && this.meetingElement) {
            this.meetingElement.nativeElement.removeEventListener(
                "rtkStatesUpdate",
                this.stateUpdateListener,
            );
        }
    }
}

For Android, attach event listeners to the meeting object to observe state changes. Use RtkMeetingRoomEventListener for meeting lifecycle events and RtkSelfEventListener for local participant state changes.

```kotlin import android.os.Bundle import android.util.Log import androidx.appcompat.app.AppCompatActivity

class MeetingActivity : AppCompatActivity() {

override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState)

// After initializing the meeting and UI Kit (see Getting Started guide), // add event listeners to observe state changes.

// Listen for meeting room state changes meeting.addMeetingRoomEventListener(object : RtkMeetingRoomEventListener { override fun onMeetingRoomJoinStarted() { Log.d("Meeting", "Join started") }