Skip to main content

Player API

The BambuserVideoView ref exposes an imperative API for controlling playback, Picture-in-Picture, and the JS ↔ player bridge.

const playerRef = useRef<BambuserVideoViewRef>(null);

Ref Methods

MethodSignatureNotes
play()() => voidResume playback.
pause()() => voidPause playback.
mute()() => voidMute audio.
unMute()() => voidUnmute audio.
startPiP()() => voidEnter Picture-in-Picture.
stopPiP()() => voidExit Picture-in-Picture.
setPiPEnabled(enabled)(boolean) => voidEnable/disable the PiP feature. Defaults to true.
invoke(fn, args)(string, string) => Promise<any>Call a player-side function. See Player Bridge.
notify(callbackKey, info)(string, NotifyInfo) => voidReply to a callback event surfaced via onEvent.
getCurrentPlayerState()() => Promise<BambuserPlayerState>Read the last-known player state.
getNativeTag()() => number | nullUnderlying native view tag. Escape hatch, rarely needed.
cleanup()() => voidTear down the player and release native resources. Irreversible. See Lifecycle.

Player Bridge: invoke & notify

The Bambuser player runs in a webview internally. Two primitives bridge JS ↔ player:

invoke(fn, args)

Call a function on the player. Returns a Promise. Use this to drive UI commands such as showing or hiding overlays, or hydrating products.

await playerRef.current?.invoke('hideUI', '');
await playerRef.current?.invoke('showUI', '');

notify(callbackKey, info)

Respond to a callback event the player surfaced via onEvent. The callbackKey comes from the event payload; info is your response.

function onEvent(e) {
const { type, data } = e.nativeEvent;
const callbackKey =
e.nativeEvent.callbackKey ?? data?.callbackKey;

if (type === 'add-to-wishlist' && callbackKey) {
const sku = Platform.OS === 'ios' ? data?.event?.sku : data?.sku;
// ... your wishlist logic
playerRef.current?.notify(callbackKey, { success: true, sku });
}
}

info accepts booleans, numbers, strings, plain objects/arrays, or null. Strings that already look like JS literals ({...}, [...], numbers, true/false/null) are passed through; anything else is JSON-encoded.

Picture-in-Picture

PiP is enabled by default. Disable per-component with setPiPEnabled(false), or trigger it manually with startPiP() / stopPiP().

playerRef.current?.startPiP();
playerRef.current?.stopPiP();
playerRef.current?.setPiPEnabled(false);

Platform behavior

  • iOS: PiP is view-level. Only the video floats; the rest of your app is unaffected. Provided by the SDK's pipController.

  • Android: PiP is activity-level. The entire host activity shrinks into the PiP window. You should hide non-video UI when entering PiP and restore it on exit:

    const [inPiP, setInPiP] = useState(false);

    <BambuserVideoView
    onPiPStateChanged={(e) => setInPiP(e.nativeEvent.state === 'started')}
    ...
    />
    {!inPiP && <YourAppChrome />}

See Events › onPiPStateChanged for the full list of PiP states.

Lifecycle & Cleanup

The player automatically releases native resources when the component unmounts. You normally don't need to call cleanup() yourself.

Call cleanup() explicitly when:

  • You're about to navigate away and want to free resources immediately, or
  • You want to stop the video before unmount (e.g. the user backed out via a custom gesture).

⚠️ cleanup() is irreversible. Once called, the player is gone. To play again, unmount and remount the component (or change id to force a rebuild).

useEffect(() => () => playerRef.current?.cleanup(), []);

Reading state imperatively

For type-safe state handling outside of callbacks:

import type { BambuserPlayerState } from '@bambuser/react-native-commerce-sdk';

const state: BambuserPlayerState | undefined =
await playerRef.current?.getCurrentPlayerState();

switch (state) {
case 'playing': /* ... */ break;
case 'buffering': /* ... */ break;
case 'paused': /* ... */ break;
}