---
title: Add Custom Header
description: Add a custom header to your RealtimeKit meeting UI with individual components.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/realtime/llms.txt  
> Use this file to discover all available pages before exploring further. 

[Skip to content](#%5Ftop) 

# Add Custom Header

Prerequisite

This guide builds on top of the [Build Your Own UI](https://developers.cloudflare.com/realtime/realtimekit/ui-kit/build-your-own-ui/). It is recommended to read that guide first. Portions of the code will not be repeated here for brevity.

For a complete list of available UI Kit components, refer to the [Component Library](https://developers.cloudflare.com/realtime/realtimekit/ui-kit/component-library/).

In this guide, we will learn how to add a custom header for your RealtimeKit meeting experience.

WebMobile

ReactWeb ComponentsAngular

RealtimeKit UI provides the [RtkHeader](https://developers.cloudflare.com/realtime/realtimekit/ui-kit/component-library/#rtk-header) component for a default header.

If you need additional controls, replace `RtkHeader` with individual UI Kit components and custom elements.

Import the required components:

```
import {  RtkLogo,  RtkRecordingIndicator,  RtkLivestreamIndicator,  RtkMeetingTitle,  RtkGridPagination,  RtkParticipantCount,  RtkViewerCount,  RtkClock,} from "@cloudflare/realtimekit-ui";
```

In your `RtkUIProvider` from [Build Your Own UI](https://developers.cloudflare.com/realtime/realtimekit/ui-kit/build-your-own-ui/), replace:

```
<RtkHeader />
```

with:

```
<div  style={{    display: "flex",    justifyContent: "space-between",    backgroundColor: "black",    color: "white",  }}>  <div    id="header-left"    style={{ display: "flex", alignItems: "center", height: "48px" }}  >    <RtkLogo />    <RtkRecordingIndicator />    <RtkLivestreamIndicator />  </div>  <div    id="header-center"    style={{ display: "flex", alignItems: "center", height: "48px" }}  >    <RtkMeetingTitle />  </div>  <div    id="header-right"    style={{ display: "flex", alignItems: "center", height: "48px" }}  >    <RtkGridPagination />    <RtkParticipantCount />    <RtkViewerCount />    <RtkClock />    <button onClick={handleReportBugClick}>Report Bug</button>  </div></div>
```

Define the click handler:

```
const handleReportBugClick = () => {  console.log("Report Bug Clicked");};
```

For a complete example, refer to the [custom UI example ↗](https://github.com/cloudflare/realtimekit-web-examples/tree/main/react-examples/examples/create-your-own-ui) and the [custom header component ↗](https://github.com/cloudflare/realtimekit-web-examples/blob/main/react-examples/examples/create-your-own-ui/src/components/meeting-header.tsx).

RealtimeKit UI provides the [rtk-header](https://developers.cloudflare.com/realtime/realtimekit/ui-kit/component-library/#rtk-header) component for a default header.

If you need additional controls, replace `rtk-header` with individual UI Kit components and custom elements. In the `renderJoinedScreen` function from [Build Your Own UI](https://developers.cloudflare.com/realtime/realtimekit/ui-kit/build-your-own-ui/), replace:

```
<rtk-header style="display: flex; justify-content: space-between;"></rtk-header>
```

with:

```
<div  style="display: flex; justify-content: space-between; align-items: center; height: 48px; padding: 0 12px; background-color: black; color: white;">  <div style="display: flex; align-items: center; gap: 8px;">    <rtk-logo></rtk-logo>    <rtk-recording-indicator></rtk-recording-indicator>    <rtk-livestream-indicator></rtk-livestream-indicator>  </div>
  <div style="display: flex; align-items: center;">    <rtk-meeting-title></rtk-meeting-title>  </div>
  <div style="display: flex; align-items: center; gap: 8px;">    <rtk-grid-pagination></rtk-grid-pagination>    <rtk-participant-count></rtk-participant-count>    <rtk-viewer-count></rtk-viewer-count>    <rtk-clock></rtk-clock>    <button id="report-bug-button" type="button">Report Bug</button>  </div></div>
```

Register the click handler after rendering:

JavaScript

```
document.querySelector("#report-bug-button").addEventListener("click", () => {  console.log("Report Bug Clicked");});
```

For a complete example, refer to the [custom UI example ↗](https://github.com/cloudflare/realtimekit-web-examples/tree/main/html-examples/examples/create-your-own-ui) and the [custom header component ↗](https://github.com/cloudflare/realtimekit-web-examples/blob/main/html-examples/examples/create-your-own-ui/components/meeting-header.js).

RealtimeKit UI provides the [rtk-header](https://developers.cloudflare.com/realtime/realtimekit/ui-kit/component-library/#rtk-header) component for a default header.

If you need additional controls, replace `rtk-header` with individual UI Kit components and custom elements. Create a custom header component that uses the RealtimeKit angular components directly.

#### Create Custom Header Component

custom-header.component.ts

```
import { Component, AfterViewInit } from "@angular/core";
@Component({  selector: "app-custom-header",  template: `    <div class="custom-header">      <div class="header-left">        <rtk-logo></rtk-logo>        <rtk-recording-indicator></rtk-recording-indicator>        <rtk-livestream-indicator></rtk-livestream-indicator>      </div>
      <div class="header-center">        <rtk-meeting-title></rtk-meeting-title>      </div>
      <div class="header-right">        <rtk-grid-pagination></rtk-grid-pagination>        <rtk-participant-count></rtk-participant-count>        <rtk-viewer-count></rtk-viewer-count>        <rtk-clock></rtk-clock>        <button          type="button"          class="report-bug-button"          (click)="onReportBugClick()"        >          Report Bug        </button>      </div>    </div>  `,  styles: [    `      .custom-header {        display: flex;        justify-content: space-between;        align-items: center;        height: 48px;        padding: 0 12px;        background-color: black;        color: white;      }
      .header-left,      .header-right {        display: flex;        align-items: center;        gap: 8px;      }
      .header-center {        display: flex;        align-items: center;      }
      .report-bug-button {        background: none;        border: 1px solid white;        color: white;        padding: 4px 8px;        border-radius: 4px;        cursor: pointer;        font-size: 12px;      }
      .report-bug-button:hover {        background-color: rgba(255, 255, 255, 0.1);      }    `,  ],})export class CustomHeaderComponent implements AfterViewInit {  ngAfterViewInit() {    console.log("Custom header initialized");  }
  onReportBugClick() {    console.log("Report Bug Clicked");    // Add your custom logic here    // For example: open a modal, navigate to a form, etc.  }}
```

#### Use in Your Meeting Component

In your main meeting component template, replace:

```
<rtk-header style="display: flex; justify-content: space-between;"></rtk-header>
```

with:

```
<app-custom-header></app-custom-header>
```

#### Complete Meeting Component Example

meeting.component.ts

```
import {  Component,  ElementRef,  OnInit,  OnDestroy,  ViewChild,} from "@angular/core";
@Component({  selector: "app-meeting",  template: `    <rtk-meeting #meetingComponent id="meeting-component">      <!-- Custom header replaces rtk-header -->      <app-custom-header></app-custom-header>
      <!-- Other meeting UI components -->      <rtk-grid></rtk-grid>      <rtk-sidebar></rtk-sidebar>    </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  }}
```

#### Module Configuration

Don't forget to declare your custom header component in your Angular module:

app.module.ts

```
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 { CustomHeaderComponent } from "./custom-header.component";
@NgModule({  declarations: [AppComponent, MeetingComponent, CustomHeaderComponent],  imports: [BrowserModule],  providers: [],  bootstrap: [AppComponent],  schemas: [CUSTOM_ELEMENTS_SCHEMA], // Required for RTK web components})export class AppModule {}
```

This approach gives you full control over the header layout while maintaining Angular's component architecture and event handling patterns.

RealtimeKit UI provides the `RtkMeetingHeaderView` component for a default header.

If you need additional controls, replace `RtkMeetingHeaderView` with individual UI Kit components and custom elements.

Import the required components:

Swift

```
import RealtimeKitUI
```

#### Create custom header view

Create a custom header view that uses the RealtimeKit iOS components directly:

CustomHeaderView.swift

```
import UIKitimport RealtimeKitimport RealtimeKitUI
class CustomHeaderView: UIView {    private let meeting: RealtimeKitClient
    private lazy var titleLabel: RtkMeetingTitleLabel = {        return RtkMeetingTitleLabel(meeting: meeting)    }()
    private lazy var participantCountView: RtkParticipantCountView = {        return RtkParticipantCountView(meeting: meeting)    }()
    private lazy var clockView: RtkClockView = {        return RtkClockView(meeting: meeting)    }()
    private lazy var recordingView: RtkRecordingView = {        return RtkRecordingView(meeting: meeting)    }()
    private let reportBugButton = UIButton(type: .system)
    init(meeting: RealtimeKitClient) {        self.meeting = meeting        super.init(frame: .zero)        setupUI()    }
    required init?(coder: NSCoder) {        fatalError("init(coder:) has not been implemented")    }
    private func setupUI() {        backgroundColor = .black
        // Configure report bug button        reportBugButton.setTitle("Report Bug", for: .normal)        reportBugButton.setTitleColor(.white, for: .normal)        reportBugButton.layer.borderColor = UIColor.white.cgColor        reportBugButton.layer.borderWidth = 1        reportBugButton.layer.cornerRadius = 4        reportBugButton.contentEdgeInsets = UIEdgeInsets(top: 4, left: 8, bottom: 4, right: 8)        reportBugButton.addTarget(self, action: #selector(handleReportBugTap), for: .touchUpInside)
        // Create stack views for layout        let leftStack = UIStackView(arrangedSubviews: [recordingView])        leftStack.axis = .horizontal        leftStack.spacing = 8        leftStack.alignment = .center
        let centerStack = UIStackView(arrangedSubviews: [titleLabel])        centerStack.axis = .horizontal        centerStack.alignment = .center
        let rightStack = UIStackView(arrangedSubviews: [            participantCountView,            clockView,            reportBugButton        ])        rightStack.axis = .horizontal        rightStack.spacing = 8        rightStack.alignment = .center
        let mainStack = UIStackView(arrangedSubviews: [leftStack, centerStack, rightStack])        mainStack.axis = .horizontal        mainStack.distribution = .equalSpacing        mainStack.alignment = .center        mainStack.translatesAutoresizingMaskIntoConstraints = false
        addSubview(mainStack)
        NSLayoutConstraint.activate([            mainStack.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 12),            mainStack.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -12),            mainStack.topAnchor.constraint(equalTo: topAnchor),            mainStack.bottomAnchor.constraint(equalTo: bottomAnchor),            heightAnchor.constraint(equalToConstant: 48)        ])    }
    @objc private func handleReportBugTap() {        print("Report Bug Tapped")        // Add your custom logic here    }}
```

#### Use in your meeting view controller

In your `MeetingViewController`, replace the default header with your custom header:

MeetingViewController.swift

```
import UIKitimport RealtimeKitimport RealtimeKitUI
class MeetingViewController: UIViewController {    private var meeting: RealtimeKitClient?    private var customHeader: CustomHeaderView?
    override func viewDidLoad() {        super.viewDidLoad()        setupMeeting()    }
    private func setupMeeting() {        Task {            do {                let client = try await RealtimeKitClient.init(authToken: "<PARTICIPANT_AUTH_TOKEN>")                self.meeting = client                await MainActor.run {                    setupCustomHeader(meeting: client)                }            } catch {                print("Failed to initialize meeting: \(error)")            }        }    }
    private func setupCustomHeader(meeting: RealtimeKitClient) {        let header = CustomHeaderView(meeting: meeting)        self.customHeader = header
        view.addSubview(header)        header.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([            header.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),            header.leadingAnchor.constraint(equalTo: view.leadingAnchor),            header.trailingAnchor.constraint(equalTo: view.trailingAnchor)        ])    }}
```

RealtimeKit UI provides the `RtkMeetingHeaderView` component for a default header.

If you need additional controls, replace `RtkMeetingHeaderView` with individual UI Kit components and custom elements.

Import the required components:

Kotlin

```
import com.cloudflare.realtimekit.ui.view.headers.RtkMeetingHeaderViewimport com.cloudflare.realtimekit.ui.view.RtkMeetingTitleViewimport com.cloudflare.realtimekit.ui.view.RtkParticipantCountViewimport com.cloudflare.realtimekit.ui.view.RtkClockViewimport com.cloudflare.realtimekit.ui.view.RtkRecordingIndicatorimport com.cloudflare.realtimekit.ui.view.RtkLivestreamIndicatorimport com.cloudflare.realtimekit.ui.view.RtkGridPaginatorView
```

#### Create custom header layout

Create an XML layout file for your custom header:

layout/custom\_header.xml

```
<?xml version="1.0" encoding="utf-8"?><LinearLayout    xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="48dp"    android:orientation="horizontal"    android:gravity="center_vertical"    android:paddingHorizontal="12dp"    android:background="@android:color/black">
    <!-- Left section -->    <LinearLayout        android:layout_width="0dp"        android:layout_height="wrap_content"        android:layout_weight="1"        android:orientation="horizontal"        android:gravity="start|center_vertical">
        <com.cloudflare.realtimekit.ui.view.RtkRecordingIndicator            android:id="@+id/recordingIndicator"            android:layout_width="wrap_content"            android:layout_height="wrap_content" />
        <com.cloudflare.realtimekit.ui.view.RtkLivestreamIndicator            android:id="@+id/livestreamIndicator"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:layout_marginStart="8dp" />    </LinearLayout>
    <!-- Center section -->    <com.cloudflare.realtimekit.ui.view.RtkMeetingTitleView        android:id="@+id/meetingTitle"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:textColor="@android:color/white" />
    <!-- Right section -->    <LinearLayout        android:layout_width="0dp"        android:layout_height="wrap_content"        android:layout_weight="1"        android:orientation="horizontal"        android:gravity="end|center_vertical">
        <com.cloudflare.realtimekit.ui.view.RtkGridPaginatorView            android:id="@+id/gridPaginator"            android:layout_width="wrap_content"            android:layout_height="wrap_content" />
        <com.cloudflare.realtimekit.ui.view.RtkParticipantCountView            android:id="@+id/participantCount"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:layout_marginStart="8dp" />
        <com.cloudflare.realtimekit.ui.view.RtkClockView            android:id="@+id/clock"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:layout_marginStart="8dp" />
        <Button            android:id="@+id/reportBugButton"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:layout_marginStart="8dp"            android:text="Report Bug"            android:textColor="@android:color/white"            android:background="@android:color/transparent"            style="?android:attr/borderlessButtonStyle" />    </LinearLayout></LinearLayout>
```

#### Create custom header view class

CustomHeaderView.kt

```
package com.example.meeting
import android.content.Contextimport android.util.AttributeSetimport android.util.Logimport android.view.LayoutInflaterimport android.widget.Buttonimport android.widget.LinearLayoutimport com.cloudflare.realtimekit.ui.view.RtkClockViewimport com.cloudflare.realtimekit.ui.view.RtkMeetingTitleViewimport com.cloudflare.realtimekit.ui.view.RtkParticipantCountViewimport com.cloudflare.realtimekit.ui.view.RtkRecordingIndicator
class CustomHeaderView @JvmOverloads constructor(    context: Context,    attrs: AttributeSet? = null,    defStyleAttr: Int = 0) : LinearLayout(context, attrs, defStyleAttr) {
    init {        LayoutInflater.from(context).inflate(R.layout.custom_header, this, true)        setupReportBugButton()    }
    private fun setupReportBugButton() {        findViewById<Button>(R.id.reportBugButton).setOnClickListener {            Log.d("CustomHeader", "Report Bug Clicked")            // Add your custom logic here        }    }}
```

#### Use in your meeting activity

In your `MeetingActivity`, replace the default header with your custom header:

MeetingActivity.kt

```
package com.example.meeting
import android.os.Bundleimport androidx.appcompat.app.AppCompatActivityimport com.cloudflare.realtimekit.RealtimeKitClient
class MeetingActivity : AppCompatActivity() {    private lateinit var meeting: RealtimeKitClient    private lateinit var customHeader: CustomHeaderView
    override fun onCreate(savedInstanceState: Bundle?) {        super.onCreate(savedInstanceState)        setContentView(R.layout.activity_meeting)
        customHeader = findViewById(R.id.customHeader)        initializeMeeting()    }
    private fun initializeMeeting() {        RealtimeKitClient.init(            authToken = "<PARTICIPANT_AUTH_TOKEN>",            onSuccess = { client ->                meeting = client                // Configure meeting UI            },            onError = { error ->                Log.e("Meeting", "Failed to initialize: $error")            }        )    }}
```

RealtimeKit UI provides the `RtkAppBar` widget for a default header.

If you need additional controls, replace `RtkAppBar` with individual UI Kit widgets and custom elements.

Import the required components:

Dart

```
import 'package:realtimekit/realtimekit.dart';import 'package:realtimekit_ui/realtimekit_ui.dart';
```

#### Create custom header widget

Create a custom header widget that uses the RealtimeKit Flutter components directly. These widgets must be used within an `RtkMeetingUiProvider` context:

custom\_header.dart

```
import 'package:flutter/material.dart';import 'package:realtimekit_ui/realtimekit_ui.dart';
class CustomHeader extends StatelessWidget {  final VoidCallback? onReportBugPressed;
  const CustomHeader({    Key? key,    this.onReportBugPressed,  }) : super(key: key);
  void _handleReportBugPressed() {    debugPrint('Report Bug Pressed');    onReportBugPressed?.call();  }
  @override  Widget build(BuildContext context) {    return Container(      height: 48,      color: Colors.black,      padding: const EdgeInsets.symmetric(horizontal: 12),      child: Row(        children: [          // Left section          Expanded(            child: Row(              children: const [                RtkRecordingIndicator(),                SizedBox(width: 8),                RtkLivestreamIndicator(),              ],            ),          ),
          // Center section          const RtkMeetingTitle(),
          // Right section          Expanded(            child: Row(              mainAxisAlignment: MainAxisAlignment.end,              children: [                const RtkParticipantCount(),                const SizedBox(width: 8),                const RtkClock(),                const SizedBox(width: 8),                OutlinedButton(                  onPressed: _handleReportBugPressed,                  style: OutlinedButton.styleFrom(                    foregroundColor: Colors.white,                    side: const BorderSide(color: Colors.white),                    padding: const EdgeInsets.symmetric(                      horizontal: 8,                      vertical: 4,                    ),                  ),                  child: const Text('Report Bug'),                ),              ],            ),          ),        ],      ),    );  }}
```

#### Use in your meeting screen

In your meeting screen, replace the default `RtkAppBar` with your custom header:

meeting\_screen.dart

```
import 'package:flutter/material.dart';import 'package:realtimekit/realtimekit.dart';import 'package:realtimekit_ui/realtimekit_ui.dart';import 'custom_header.dart';
class MeetingScreen extends StatefulWidget {  const MeetingScreen({Key? key}) : super(key: key);
  @override  State<MeetingScreen> createState() => _MeetingScreenState();}
class _MeetingScreenState extends State<MeetingScreen> {  RealtimeKitClient? _meeting;
  @override  void initState() {    super.initState();    _initializeMeeting();  }
  Future<void> _initializeMeeting() async {    try {      final meeting = await RealtimeKitClient.init(        authToken: '<PARTICIPANT_AUTH_TOKEN>',      );      setState(() {        _meeting = meeting;      });    } catch (e) {      debugPrint('Failed to initialize meeting: $e');    }  }
  void _handleReportBug() {    // Add your custom logic here    showDialog(      context: context,      builder: (context) => AlertDialog(        title: const Text('Report Bug'),        content: const Text('Bug reporting form goes here.'),        actions: [          TextButton(            onPressed: () => Navigator.pop(context),            child: const Text('Close'),          ),        ],      ),    );  }
  @override  Widget build(BuildContext context) {    if (_meeting == null) {      return const Scaffold(        body: Center(child: CircularProgressIndicator()),      );    }
    return RtkMeetingUiProvider(      meeting: _meeting!,      child: Scaffold(        body: Column(          children: [            // Custom header replaces RtkAppBar            CustomHeader(onReportBugPressed: _handleReportBug),
            // Meeting grid            const Expanded(child: RtkGrid()),
            // Control bar            const RtkControlBar(),          ],        ),      ),    );  }
  @override  void dispose() {    _meeting?.leave();    super.dispose();  }}
```

RealtimeKit UI provides the `RtkHeader` component for a default header.

If you need additional controls, replace `RtkHeader` with individual UI Kit components and custom elements.

Import the required components:

```
import {  RtkLogo,  RtkRecordingIndicator,  RtkLiveStreamIndicator,  RtkMeetingTitle,  RtkGridPagination,  RtkParticipantCount,  RtkClock,} from "@cloudflare/realtimekit-react-native-ui";import {  useRealtimeKitMeeting,  useRealtimeKitSelector,} from "@cloudflare/realtimekit-react-native";
```

#### Create custom header component

Create a custom header component that uses the RealtimeKit React Native components directly. Each component requires the `meeting` instance as a prop, which you obtain via the `useRealtimeKitMeeting` hook. The component waits for `roomJoined` before rendering to ensure the meeting state is ready:

CustomHeader.tsx

```
import React from "react";import { View, TouchableOpacity, Text, StyleSheet } from "react-native";import {  RtkLogo,  RtkRecordingIndicator,  RtkLiveStreamIndicator,  RtkMeetingTitle,  RtkGridPagination,  RtkParticipantCount,  RtkClock,} from "@cloudflare/realtimekit-react-native-ui";import {  useRealtimeKitMeeting,  useRealtimeKitSelector,} from "@cloudflare/realtimekit-react-native";
interface CustomHeaderProps {  onReportBugPress?: () => void;}
export function CustomHeader({ onReportBugPress }: CustomHeaderProps) {  const { meeting } = useRealtimeKitMeeting();  const { roomJoined } = useRealtimeKitSelector((state) => state.self);
  const handleReportBugPress = () => {    console.log("Report Bug Pressed");    onReportBugPress?.();  };
  if (!roomJoined) {    return null;  }
  return (    <View style={styles.container}>      {/* Left section */}      <View style={styles.leftSection}>        <RtkLogo />        <RtkRecordingIndicator meeting={meeting} />        <RtkLiveStreamIndicator meeting={meeting} />      </View>
      {/* Center section */}      <View style={styles.centerSection}>        <RtkMeetingTitle meeting={meeting} />      </View>
      {/* Right section */}      <View style={styles.rightSection}>        <RtkGridPagination meeting={meeting} />        <RtkParticipantCount meeting={meeting} />        <RtkClock meeting={meeting} />        <TouchableOpacity          style={styles.reportBugButton}          onPress={handleReportBugPress}        >          <Text style={styles.reportBugText}>Report Bug</Text>        </TouchableOpacity>      </View>    </View>  );}
const styles = StyleSheet.create({  container: {    flexDirection: "row",    justifyContent: "space-between",    alignItems: "center",    height: 48,    paddingHorizontal: 12,    backgroundColor: "black",  },  leftSection: {    flexDirection: "row",    alignItems: "center",    gap: 8,  },  centerSection: {    flexDirection: "row",    alignItems: "center",  },  rightSection: {    flexDirection: "row",    alignItems: "center",    gap: 8,  },  reportBugButton: {    borderWidth: 1,    borderColor: "white",    borderRadius: 4,    paddingHorizontal: 8,    paddingVertical: 4,  },  reportBugText: {    color: "white",    fontSize: 12,  },});
```

Note

`useRealtimeKitMeeting` and `useRealtimeKitSelector` must be called inside a component rendered within a `RealtimeKitProvider`. The `roomJoined` guard ensures the header only renders once the participant has successfully joined the meeting. `meeting.join()` is called from the parent `MeetingScreen` so that the join lifecycle is managed in one place.

#### Use in your meeting screen

In your meeting screen, replace the default `RtkHeader` with your custom header:

MeetingScreen.tsx

```
import React, { useEffect } from "react";import { View, StyleSheet } from "react-native";import {  RealtimeKitProvider,  useRealtimeKitClient,} from "@cloudflare/realtimekit-react-native";import {  RtkGrid,  RtkControlbar,  RtkDialogManager,} from "@cloudflare/realtimekit-react-native-ui";import { CustomHeader } from "./CustomHeader";
function MeetingContainer() {  const [meeting, initMeeting] = useRealtimeKitClient();
  useEffect(() => {    initMeeting({      authToken: "<PARTICIPANT_AUTH_TOKEN>",    });  }, [initMeeting]);
  useEffect(() => {    if (!meeting) return;    meeting.join();    // eslint-disable-next-line react-hooks/exhaustive-deps  }, [meeting]);
  if (!meeting) {    return null;  }
  return (    <RealtimeKitProvider value={meeting}>      <View style={styles.container}>        {/* Custom header replaces RtkHeader */}        <CustomHeader          onReportBugPress={() => console.log("Report bug pressed")}        />
        {/* Meeting grid */}        <View style={styles.gridContainer}>          <RtkGrid meeting={meeting} />        </View>
        {/* Control bar */}        <RtkControlbar meeting={meeting} />      </View>
      {/* Required components */}      <RtkDialogManager meeting={meeting} />    </RealtimeKitProvider>  );}
export function MeetingScreen() {  return <MeetingContainer />;}
const styles = StyleSheet.create({  container: {    flex: 1,    backgroundColor: "black",  },  gridContainer: {    flex: 1,  },});
```

Note

You must include `RtkDialogManager`. If you leave it out, features like settings toggles will not work.

```json
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/realtime/realtimekit/ui-kit/custom-header/#page","headline":"Add Custom Header · Cloudflare Realtime docs","description":"Add a custom header to your RealtimeKit meeting UI with individual components.","url":"https://developers.cloudflare.com/realtime/realtimekit/ui-kit/custom-header/","inLanguage":"en","image":"https://developers.cloudflare.com/dev-products-preview.png","dateModified":"2026-04-22","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/realtime/","name":"Realtime"}},{"@type":"ListItem","position":3,"item":{"@id":"/realtime/realtimekit/","name":"RealtimeKit"}},{"@type":"ListItem","position":4,"item":{"@id":"/realtime/realtimekit/ui-kit/","name":"Build using UI Kit"}},{"@type":"ListItem","position":5,"item":{"@id":"/realtime/realtimekit/ui-kit/custom-header/","name":"Add Custom Header"}}]}
```
