⚛️ React Native
@bbb-mobile/bigbluebutton-mobile-sdk is the meeting client as a React Native package: one full-screen component per meeting, with the same contract as the native SDKs (join URL in, leave reason out).
It is a pre-resolved source tree of the BigBlueButton mobile client: the client's packages plus the bigbluebutton-html5 files it uses, with every import rewritten to a relative path. Your app needs neither a custom Metro resolver nor a bigbluebutton-html5 checkout. The three Expo native modules it uses (@bbb-mobile/pip, @bbb-mobile/downloads, @bbb-mobile/sdk-host) are separate npm packages that the main package depends on at the same version.
| Package | @bbb-mobile/bigbluebutton-mobile-sdk 0.1.0 on npm |
| Requirements | React Native 0.86.x (0.86.3 or later), Expo modules installed in the app (Expo SDK 57, expo 57.0.22 or later), iOS 16.4+, Android minSdk 24 |
The exact versions the package was built against are in the version matrix.
📦 1. Install
npm install @bbb-mobile/bigbluebutton-mobile-sdk
Then add the Expo modules runtime to the native projects (see the note below).
npm puts the three @bbb-mobile/* native modules at the top level of node_modules, where Expo autolinking finds them (ls node_modules/@bbb-mobile lists downloads, pip and sdk-host). If a package manager nests them under the main package instead, add them to your app's dependencies at the main package's version.
npx install-expo-modules would normally add the Expo modules runtime, but its latest release (0.16) stops at Expo SDK 56 and exits with "Unable to find compatible Expo SDK version" on React Native 0.86. Until it catches up, wire the runtime by hand, as described in the Android and iOS sections below.
🔗 2. Peer dependencies
npm installs the package's peer dependencies, the libraries with native code, at the ranges it declares: @livekit/react-native, @livekit/react-native-webrtc, expo-*, react-native-reanimated, react-native-gesture-handler, react-native-safe-area-context, react-native-svg, react-native-webview, react-native-mmkv, react-native-view-shot and react-native-get-random-values.
- Two of them need their own peers in your app:
react-native-worklets(reanimated 4) andreact-native-nitro-modules(mmkv 4). - Yarn v1 does not install peers: add them yourself.
@livekit/components-react and livekit-client are singletons shared with @livekit/react-native, and must resolve to one copy in your app. A second, nested copy of @livekit/components-react is a second React room context: the meeting then fails at startup with "No room provided", restarts its session in a loop and never subscribes a camera or audio track.
Check with npm ls @livekit/components-react livekit-client and run npm dedupe if a copy is nested. @livekit/components-react also lists react-dom as a peer: pin react-dom to your app's exact react version.
🧪 3. Babel and patches
The React Native preset does not transform export * as ns from (used by htmlparser2, a dependency of the package) and does not include the worklets plugin. Add both once:
npm install -D @babel/plugin-transform-export-namespace-from
// babel.config.js
module.exports = {
presets: ['module:@react-native/babel-preset'],
plugins: [
'@babel/plugin-transform-export-namespace-from',
'react-native-worklets/plugin', // reanimated 4 / mmkv 4 worklets; must be last
],
};
Without the worklets plugin the first meeting crashes with Cannot read property 'code' of undefined from react-native-worklets.
Then apply the patches shipped in node_modules/@bbb-mobile/bigbluebutton-mobile-sdk/patches/ (two for LiveKit, one for react-native-view-shot against React Native 0.86's prebuilt core): copy them into your app's patches/ folder and run patch-package (for example as a postinstall script).
🤖 4. Android
Until install-expo-modules supports Expo SDK 57, the Expo modules runtime is wired by hand in five files:
android/settings.gradle— inpluginManagement,includeBuildthe Expo Gradle plugins: theandroid/expo-gradle-pluginfolder ofexpo-modules-autolinking, resolved withnode --print "require.resolve('expo-modules-autolinking/package.json', { paths: [require.resolve('expo/package.json')] })". Apply theexpo-autolinking-settingsplugin next tocom.facebook.react.settings, autolink withautolinkLibrariesFromCommand(expoAutolinking.rnConfigCommand), then callexpoAutolinking.useExpoModules()andexpoAutolinking.useExpoVersionCatalog().android/build.gradle—apply plugin: "expo-root-project"beforecom.facebook.react.rootproject, and the JitPack repository (https://www.jitpack.io, for LiveKit's audioswitch) inallprojects.repositories.MainApplication.kt— the React host comes fromExpoReactHostFactory.getDefaultReactHost(...)withjsMainModulePath = "index";onCreatecallsApplicationLifecycleDispatcher.onApplicationCreate(this), and anonConfigurationChangedoverride callsApplicationLifecycleDispatcher.onConfigurationChanged(this, newConfig).MainActivity.kt—createReactActivityDelegate()returnsReactActivityDelegateWrapper(this, BuildConfig.IS_NEW_ARCHITECTURE_ENABLED, DefaultReactActivityDelegate(this, mainComponentName, fabricEnabled)).AndroidManifest.xml— the meeting's permissions (microphone, camera, Bluetooth, foreground service types, notifications, media projection) and, on the main activity,android:supportsPictureInPicture="true"andlocale|layoutDirectionadded toandroid:configChanges.
minSdk is 24. The pickFirsts for libc++_shared.so / libfbjni.so come from the React Native Gradle plugin, and the SDK's meeting foreground service is merged from @bbb-mobile/sdk-host's manifest: nothing to add for either.
🍎 5. iOS
The Podfile needs Expo's autolinking, and the app delegate becomes an ExpoAppDelegate:
ios/Podfile—require File.join(File.dirname(`node --print "require.resolve('expo/package.json')"`), "scripts/autolinking")above the React Nativereact_native_podsrequire;use_expo_modules!first in the app target;use_native_modules!gets Expo's React Native config command as an argument array:node --no-warnings --eval "require('expo/bin/autolinking')" expo-modules-autolinking react-native-config --json --platform ios.AppDelegate.swift—AppDelegatesubclassesExpoAppDelegateand starts React Native through anExpoReactNativeFactory, whose delegate subclassesExpoReactNativeFactoryDelegate(bundle URL,RCTAppDependencyProvider()asdependencyProvider).application(_:didFinishLaunchingWithOptions:)is anoverridethat returnssuper.application(application, didFinishLaunchingWithOptions: launchOptions).
Set the deployment target to 16.4, run pod install, and add the Info.plist keys and UIBackgroundModes: audio from the 🍎 iOS guide.
💻 6. Use
import 'react-native-get-random-values'; // first import of the app
import {
BigBlueButtonMeeting,
configureBigBlueButtonMeeting,
type LeaveInfo,
} from '@bbb-mobile/bigbluebutton-mobile-sdk';
configureBigBlueButtonMeeting({ clientType: 'NATIVE_MOBILE', appVersion: '2.3.4' }); // once, at startup
function MeetingScreen({ url, onDone }: { url: string; onDone: (info: LeaveInfo) => void }) {
return (
<BigBlueButtonMeeting
joinUrl={url}
meetingName="Weekly"
onJoined={() => {}}
onLeave={(info) => onDone(info)} // { reason: 'left' | 'ended' | 'error' | 'unsupported', detail? }
openLogoutUrl={false} // stay in the app when the meeting ends
gestureRoot={false} // when the app already has a GestureHandlerRootView
/>
);
}
- Render the component full-screen and unmount it on
onLeave. One React Native runtime hosts one meeting at a time. - Props mirror the native join options:
theme,pictureInPicture,screensharePresenter,embedMode,largeScreenLayoutanddevLogging, plusleaveSignal(host-initiated leave) andafterLeave. See the 📘 API reference. screensharePresenteris on by default on Android and opt-in on iOS: pass it only when your app ships the broadcast extension (🖥️ iOS screen sharing).onErroris accepted but not called in 0.1.0; failures arrive throughonLeavewithreason: 'error'.configureBigBlueButtonMeetinginstalls the html5 global shims (window,document,navigator.mediaDevices, …) process-wide. PassappVersionexplicitly: without it the user agent shows the Expo configversion, or0.0.0.- Apps that use
react-native-webrtcmust switch to@livekit/react-native-webrtc(same JavaScript API; the two cannot coexist). PassregisterWebRtcGlobals: falseif you register the WebRTC globals yourself.
TypeScript: the package ships a hand-written index.d.ts with the public API (BigBlueButtonMeeting, configureBigBlueButtonMeeting, LeaveInfo and the props), so your tsc does not walk the pre-resolved sources.