React Virtual Backgrounds in Standard, Hybrid, and Headless UIs
In ReactJS, virtual backgrounds change the video track that MediaSFU publishes. Standard UI owns the complete workflow. Hybrid UI reuses the SDK modal against the same room engine. Fully custom UI owns the controls while calling the exported processing helpers.
Choose the guide for your SDK
| SDK | Supplied background surface | Fully custom boundary |
|---|---|---|
| ReactJS 4.4.5 | ModernBackgroundModal and BackgroundModal | applyVirtualBackground, applyBackgroundBlur, clearVirtualBackground, getLocalVideoStream |
| Angular 2.5.2 | BackgroundModal and LaunchBackground | Use Angular's room/background services; no React helper API |
| Vue 1.3.2 | BackgroundModal and useBackgroundModal | Use Vue's room state; no React helper API |
| React Native 2.5.2 and Expo 2.6.2 | Native background modal displays an unsupported-state message | Do not advertise image or blur processing on native mobile from these packages |
| Flutter 2.4.4 | Background controls in the supplied media settings | Flutter background state and platform processor; test each target platform |
| Android/Kotlin 1.0.9 | Compose/platform virtual-background processing | VirtualBackgroundProcessor and current room state; test on target devices |
| Swift 0.1.7 | On-device backgrounds (blur, image, color) in the hosted room's media settings | Use the hosted room; there is no separate Swift background API |
| Unity preview.2 | No complete standalone UI workflow | Use the package's supported room/media path instead |
| Shared core 1.3.3 | No UI; your framework renders the controls | applyVirtualBackground, applyBackgroundBlur, clearVirtualBackground, getLocalVideoStream with the complete room parameters |
The code below is for ReactJS only. For another package, start with its device and background section and SDK guide; do not apply React component props to another UI framework.
Standard: let the room own the feature
Mount the current complete room and open its media/background settings. The room owns camera acquisition, preview, save, apply, publish, restore, removal, and cleanup. A saved selection can be retained while the camera is off and applied when the room publishes the camera later.
import {ModernMediasfuGeneric} from 'mediasfu-reactjs';
export function StandardRoom() {
return <ModernMediasfuGeneric />;
}
Hybrid: use the room-owned modal lifecycle
Pass the latest room parameters after each room-state publication. Opening and closing use that room's update function. Keep the component mounted for the room's lifetime so camera-off preview state and automatic restoration are preserved.
import type {ComponentProps} from 'react';
import {ModernBackgroundModal} from 'mediasfu-reactjs';
type BackgroundRoom = ComponentProps<typeof ModernBackgroundModal>['parameters'] & {
isBackgroundModalVisible: boolean;
updateIsBackgroundModalVisible: (visible: boolean) => void;
};
export function BackgroundPanel({parameters}: {parameters: BackgroundRoom}) {
return <section aria-label="Camera background">
<button onClick={() => parameters.updateIsBackgroundModalVisible(true)}>
Choose background
</button>
<ModernBackgroundModal
isVisible={parameters.isBackgroundModalVisible === true}
onClose={() => parameters.updateIsBackgroundModalVisible(false)}
parameters={parameters}
renderMode="sidebar"
/>
</section>;
}
React supports renderMode="modal", "sidebar", or "inline" on
ModernBackgroundModal. These are presentation choices, not separate modal
lifecycles. Do not add a competing local isOpen state or conditionally
unmount the component whenever it is hidden.
With the camera off, the modal may acquire temporary preview media. Save stores the selection for a later camera start; it does not itself prove that video is being published. Let the modal release preview resources and let the room apply the saved selection when camera publication begins.
Fully headless: own controls, not transport internals
The React helper requires a live camera for a published replacement. Serialize
actions and show every returned error. Pass image for an image background;
use the exported applyBackgroundBlur for a person-aware blur. Both
use the SDK-owned processed-track lifecycle, rather than a second camera
producer.
import {useState} from 'react';
import {
applyVirtualBackground,
applyBackgroundBlur,
clearVirtualBackground,
getCurrentParams,
type HeadlessParameters,
} from 'mediasfu-reactjs';
export function BackgroundActions({parameters, image}: {
parameters: HeadlessParameters;
image: string;
}) {
const [busy, setBusy] = useState(false);
const [notice, setNotice] = useState('');
async function change(mode: 'image' | 'blur' | 'remove') {
if (busy) return;
const live = getCurrentParams({parameters});
if (mode !== 'remove' && !live.videoAlreadyOn) {
setNotice('Turn on the camera before applying a background.');
return;
}
setBusy(true);
try {
const result = mode === 'remove'
? await clearVirtualBackground({parameters: live})
: mode === 'blur'
? await applyBackgroundBlur({parameters: live, publish: true})
: await applyVirtualBackground({parameters: live, image, publish: true});
setNotice(result.ok ? 'Background updated.' : result.error);
} finally {
setBusy(false);
}
}
return <section>
<button disabled={busy} onClick={() => void change('image')}>Use image</button>
<button disabled={busy} onClick={() => void change('blur')}>Blur background</button>
<button disabled={busy} onClick={() => void change('remove')}>Remove background</button>
<p role="status">{notice}</p>
</section>;
}
publish: true replaces the running video producer's track. publish: false
is preview/local processing only. Install the optional
@mediapipe/selfie_segmentation dependency and make its model assets available
under your Content Security Policy when using React's background pipeline.
React's classic and modern modals, its headless helper, and Vue's background
modal enable best-effort hidden-tab processing by default. Their
keepProcessingWhenHidden option can disable it. Browsers may still throttle
timers, capture, or rendering in hidden tabs; do not promise uninterrupted
processed video. Check return-from-background behavior in every supported
browser. Do not assume that a native SDK has this browser option.
Always render the processed local stream first
MediaSFU's own prepopulateUserMedia and addVideosGrid logic uses this
precedence for the self view:
const selfView = keepBackground && virtualStream
? virtualStream
: localStreamVideo;
The safer React headless equivalent is
getLocalVideoStream({parameters: latestParameters}), which also rejects an
ended or disabled local video track. A custom local tile that always renders
localStreamVideo shows the raw camera even though remote users receive the
processed producer track. That mismatch is a rendering bug in the custom tile,
not evidence that publication failed.
On every parameter or media publication, reattach the selected stream when its identity changes. Keep the self view muted. Never feed the processed output back into the segmentation input.
Removal and teardown
- Clear the helper-owned pipeline before switching cameras, stopping the camera, leaving, or replacing the room session.
- Stop temporary preview tracks and animation loops that your application created.
- Remove the saved selection only when the person explicitly chooses to clear it; camera-off alone may preserve it for the next publish.
- Do not call the headless clear helper against a modal-owned pipeline.
- Test camera initially off, preview, save, later camera start, apply, remote reception, local rendering, removal, camera restart, and leave.
Continue with device and translation availability by SDK, rendered media, and leave and cleanup.