React Native Secure Room Operations
This guide shows a small, reproducible room lifecycle for mediasfu-reactnative 2.4.0: create or join through your backend, observe participants, distinguish device permission from active media, recover from request failures, and clean up app-owned resources.
The included example type-checks against the package's generated 2.4.0 declarations. Its local tests exercise request shaping and state predicates without contacting MediaSFU or opening a device. Run the final integration on physical Android and iOS devices before shipping.
Keep credentials on your backend
Your mobile bundle is inspectable. Do not place a reusable MediaSFU API key in application code, build configuration, over-the-air updates, or device storage.
Inject backend-backed room functions into the supplied room component:
const adapters = createBackendRoomAdapters(
appFetch,
runtimeConfig.applicationBackendBaseUrl,
);
<ModernMediasfuGeneric
connectMediaSFU
createMediaSFURoom={adapters.createMediaSFURoom}
joinMediaSFURoom={adapters.joinMediaSFURoom}
returnUI
/>
React Native has no same-origin application server. Pass an absolute HTTPS URL for your application backend and an injected HTTP client that applies the current user's app authentication. Keep reusable MediaSFU credentials on the backend; do not embed them in the mobile client or this URL.
The adapter deliberately ignores the credential-shaped arguments passed by the component. It sends only the requested room payload to your authenticated application routes:
import type {
CreateRoomOnMediaSFUType,
JoinRoomOnMediaSFUType,
} from 'mediasfu-reactnative';
const backendBaseUrl = runtimeConfig.applicationBackendBaseUrl;
const createUrl = new URL('rooms/create', backendBaseUrl).toString();
const joinUrl = new URL('rooms/join', backendBaseUrl).toString();
// appFetch is your configured client. It adds the signed-in user's short-lived
// application session without exposing a reusable MediaSFU credential.
export const createMediaSFURoom: CreateRoomOnMediaSFUType = async ({ payload }) => {
const response = await appFetch(createUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
});
if (!response.ok) throw new Error(`Create failed: ${response.status}`);
return response.json();
};
export const joinMediaSFURoom: JoinRoomOnMediaSFUType = async ({ payload }) => {
const response = await appFetch(joinUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
});
if (!response.ok) throw new Error(`Join failed: ${response.status}`);
return response.json();
};
Configure applicationBackendBaseUrl as an absolute HTTPS base such as https://api.example.test/. The complete local example rejects relative and non-HTTPS values, adds typed error classification, and keeps networking injectable for deterministic tests.
Your backend remains responsible for user authentication, allowed room type, capacity and duration limits, host authority, rate limiting, and the protected MediaSFU request.
Observe the room without inventing success
Use updateSourceParameters to receive the latest room state. Treat each state separately:
- A participant name in
participantsconfirms a state update, not remote media playback. - Microphone permission confirms access to the device, not an active audio producer.
audioAlreadyOn === trueorvideoAlreadyOn === trueis a useful UI observation, but verify on another device that the intended media is actually received.
The example's mediaReadiness predicate requires both granted permission and an explicit active-media observation. This prevents the UI from reporting "ready" immediately after a permission prompt.
Handle failures deliberately
| Result | User experience | Retry policy |
|---|---|---|
401 or 403 | Explain that the session or role is not authorized | Re-authenticate or request access; do not loop |
404 | Explain that the room is unavailable | Ask for a corrected room or invitation |
429 | Explain that requests are temporarily limited | Retry after backoff |
5xx or network interruption | Preserve safe user input and show a reconnect action | Retry with bounded backoff |
| Device permission denied | Keep the user in control and link to platform settings | Retry only after the user changes permission |
Never fall back to a client-held API key after a backend error.
Validate on real devices
For both Android and iOS, verify:
- First-run microphone and camera permission.
- Denial, later approval in system settings, and app resume.
- Local microphone and camera start/stop.
- Remote audio and video on a second device.
- Background/foreground transitions and route changes such as speaker, earpiece, and Bluetooth.
- Screen-share availability and stop behavior on each supported OS version.
- Network loss, reconnect, participant leave, and host-ended room behavior.
A simulator can help with layout and state tests, but it cannot validate capture, audio routing, screen sharing, or teardown. Test those behaviors on physical devices.
Leave and clean up
Use the supplied role-aware exit UI for the visible leave action. After a participant leaves, your application should also:
- stop app-owned local tracks;
- remove app-owned subscriptions and timers;
- close the room screen only after the leave action is accepted or a safe fallback is shown;
- clear participant and media snapshots so a later room cannot display stale state.
A headless host can end the room or leave it running through the current hook:
import { Button } from 'react-native';
import { useMediasfuHeadless } from 'mediasfu-reactnative';
export function HostExitButtons() {
const room = useMediasfuHeadless();
return <>
<Button title="End room" onPress={() => void room.controls.leave(false, true)} />
<Button title="Leave and keep room open" onPress={() => void room.controls.leave(false, false)} />
</>;
}
The second argument defaults to true. Await the chosen action before closing
the screen; a host who preserved the room returns through the normal authorized
join flow.
Build and test your application, then verify actual track shutdown and room cleanup on physical devices. A JavaScript test cannot prove native capture, audio routing, remote playback, or operating-system cleanup.
Next, use the React Native SDK guide for native project setup and Secure backend proxy for the server-side boundary.