Customize MediaSFU UI by SDK
Open the section for your SDK. Each package has its own component model and override types; there is no universal uiOverrides syntax.
Choose your SDK
| SDK | Built-in UI customization | Full app-owned UI |
|---|---|---|
| ReactJS | MediasfuUICustomOverrides, cards, customComponent | returnUI={false} and sourceParameters |
| Angular | MediasfuUICustomOverrides, Angular component replacements | returnUI, room parameters, Angular templates |
| Vue | MediasfuUICustomOverrides, Vue component replacements | returnUI, customComponent, room parameters |
| React Native | Native uiOverrides, custom cards and workspace | returnUI={false} and room parameters |
| Expo | Native uiOverrides, custom cards and workspace | returnUI={false} and room parameters |
| Flutter | MediasfuUICustomOverrides, builders and custom workspace | returnUI: false and MediasfuParameters |
| Android | Compose MediasfuUiOverrides and app composables | App-owned Compose workspace and room state |
| Kotlin Multiplatform | Compose MediasfuUiOverrides on supported UI targets | App-owned target UI and shared room state |
| Swift / Apple | App-owned container around the hosted room controller | App-owned SwiftUI/UIKit shell |
| Unity | App-owned scene and controls | MediaSfuClient is already UI-independent |
| Shared core | No rendered UI is supplied | Your framework owns every visible surface |
ReactJS 4.3.2
Start with a targeted replacement. This keeps the room lifecycle and the other supplied surfaces intact.
import {
MediasfuGeneric,
type MediasfuUICustomOverrides,
} from 'mediasfu-reactjs';
const uiOverrides: MediasfuUICustomOverrides = {
alert: {
render: ({visible, message, onHide}) =>
visible ? (
<button type="button" role="alert" onClick={onHide}>
{message}
</button>
) : null,
},
};
export function BrandedRoom() {
return <MediasfuGeneric uiOverrides={uiOverrides} />;
}
Use videoCard, audioCard, audioGrid, flexibleGrid, pagination, modal, and function override slots only when that surface needs to change. If several room responsibilities move into your code, follow the complete custom UI checklist.
Angular 2.3.2
Angular accepts Angular component types through its own override map.
import {Component} from '@angular/core';
import {
MediasfuUICustomOverrides,
MediasfuGeneric,
} from 'mediasfu-angular';
@Component({
selector: 'app-branded-alert',
standalone: true,
template: '<p role="alert">A room action needs your attention.</p>',
})
export class BrandedAlertComponent {}
@Component({
selector: 'app-room',
standalone: true,
imports: [MediasfuGeneric],
template: '<app-mediasfu-generic [uiOverrides]="uiOverrides" />',
})
export class RoomComponent {
readonly uiOverrides: MediasfuUICustomOverrides = {
alert: {component: BrandedAlertComponent},
};
}
Use Angular templates and components for replacements. Do not paste React render functions into an Angular project.
Vue 1.1.2
Vue accepts Vue components in its typed override map.
<script setup lang="ts">
import {defineComponent, h} from 'vue';
import {
MediasfuGeneric,
type MediasfuUICustomOverrides,
} from 'mediasfu-vue';
const BrandedAlert = defineComponent({
props: {visible: Boolean, message: String},
setup: (props) => () =>
props.visible ? h('p', {role: 'alert'}, props.message) : null,
});
const uiOverrides: MediasfuUICustomOverrides = {
alert: {component: BrandedAlert},
};
</script>
<template>
<MediasfuGeneric :ui-overrides="uiOverrides" />
</template>
Keep participant audio, video, pagination, alerts, and exit behavior mounted when replacing a larger Vue workspace.
React Native 2.4.2
React Native exposes its own native override components. Use React Native primitives, not browser elements.
import React from 'react';
import {Text} from 'react-native';
import {
MediasfuGeneric,
type MediasfuUICustomOverrides,
} from 'mediasfu-reactnative';
const uiOverrides: MediasfuUICustomOverrides = {
alert: {
render: ({visible, message}) =>
visible ? <Text accessibilityRole="alert">{message}</Text> : null,
},
};
export const Room = () => <MediasfuGeneric uiOverrides={uiOverrides} />;
Retest permissions, audio routes, camera switching, backgrounding, and screen capture on physical devices after replacing media controls.
Expo 2.5.2
Expo uses the package's React Native override types.
import React from 'react';
import {Text} from 'react-native';
import {
MediasfuGeneric,
type MediasfuUICustomOverrides,
} from 'mediasfu-reactnative-expo';
const uiOverrides: MediasfuUICustomOverrides = {
alert: {
render: ({visible, message}) =>
visible ? <Text accessibilityRole="alert">{message}</Text> : null,
},
};
export const Room = () => <MediasfuGeneric uiOverrides={uiOverrides} />;
Rebuild the development client when a customization changes native media or permission configuration.
Flutter 2.3.1
Flutter provides replacement and wrapper builders. This example preserves the default alert while applying the app theme.
import 'package:flutter/material.dart';
import 'package:mediasfu_sdk/mediasfu_sdk.dart';
final roomOverrides = MediasfuUICustomOverrides(
alert: ComponentOverride<AlertComponentOptions>(
render: (context, options, defaultBuilder) => Theme(
data: Theme.of(context).copyWith(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.indigo),
),
child: defaultBuilder(context, options),
),
),
);
Pass roomOverrides as uiOverrides in the MediasfuGenericOptions used by your room. Flutter also exposes custom cards and a custom workspace builder.
Android 1.0.6
Android uses the Compose override model from the Android room artifact.
import androidx.compose.material3.Surface
import com.mediasfu.sdk.ui.mediasfu.ComponentOverride
import com.mediasfu.sdk.ui.mediasfu.MediasfuUiOverrides
val roomOverrides = MediasfuUiOverrides(
participantsModal = ComponentOverride(
render = { props, defaultContent ->
Surface { defaultContent(props) }
},
),
)
Pass roomOverrides to MediasfuGenericOptions.uiOverrides. Keep permission requests and Android media lifecycle handling outside the visual override.
Kotlin Multiplatform 1.0.6
Compose targets use MediasfuUiOverrides, the same typed model shown in the Android example. Other targets keep their native presentation layer and share room state and operations through the KMP package.
import com.mediasfu.sdk.ui.mediasfu.MediasfuGenericOptions
fun withAppTheme(options: MediasfuGenericOptions): MediasfuGenericOptions =
options.copy(uiOverrides = roomOverrides)
Pass the fully configured options from the Kotlin room guide; copying them preserves their secure create/join callbacks and room configuration.
Swift and Apple platforms 0.1.3
The Apple package does not publish the web-style uiOverrides map. Your app owns the SwiftUI or UIKit container around the hosted room controller. Place booking details, navigation, and app actions around the controller, and use the documented bridge controls for microphone, camera, and screen share.
import UIKit
func mountRoom(_ room: UIViewController, in parent: UIViewController) {
parent.addChild(room)
parent.view.addSubview(room.view)
room.view.frame = parent.view.bounds
room.didMove(toParent: parent)
}
Unity 0.1.0-preview.2
Unity supplies a UI-independent client, so the scene is already yours. Bind buttons and renderers to MediaSfuClient rather than looking for a component override prop.
using System.Threading.Tasks;
using MediaSFU.Unity;
public static class RoomControls
{
public static async Task ToggleCameraAsync(
MediaSfuClient client,
bool enabled)
{
await client.SetCameraEnabledAsync(enabled);
}
}
Unsubscribe client events and dispose scene-owned media resources when leaving the room.
Shared core 1.2.0
mediasfu-shared renders nothing. Your framework owns participant cards, audio elements, grids, controls, modals, alerts, accessibility, and cleanup. Use the shared-core room guide before building the visible workspace.
Release checklist
- Start from the supplied room and replace one surface at a time.
- Keep remote audio mounted even when no video tile is visible.
- Preserve permission and transport errors in the app's alert surface.
- Test pagination, paused producers, screen-share stop, leave, and a second call.
- Run the selected SDK's own build and target-device checks.