Bunny Stream is the React Native SDK for Bunny's video platform. It wraps the official iOS and Android SDKs with one TypeScript API for video management, uploads, playback, and live broadcasting.
- Video playback: native player with adaptive streaming, captions, customizable controls, and resume positions.
- Live streaming: live playback with countdowns, pre-stream trailers, DVR, and transition to the recording.
- Camera recording and broadcasting: record videos or broadcast live with automatic reconnect and primary/backup failover.
- Resumable uploads: TUS uploads with progress events, pause, resume, and cancellation.
- Library management: typed APIs for videos, collections, live streams, and analytics.
- Expo support: a config plugin for development builds.
- Integration guides — setup, Expo, playback, uploads, broadcasting, and content management.
- Troubleshooting — common integration issues and platform-specific behavior.
- Platform notes — platform-specific options and limitations.
- React Native example — playback, uploads, library management, and live streaming; see setup instructions.
- Expo example — a minimal app using the config plugin.
- Bunny Stream documentation.
- llms.txt — integration cheat sheet for AI agents: setup requirements, common pitfalls, and canonical snippets.
- React Native with the New Architecture enabled (TurboModules and Fabric). The example app uses React Native 0.86.2.
- Expo (if used): SDK 52+ with a development build. Expo Go is not supported.
- iOS: Xcode and CocoaPods; use the minimum iOS deployment target required by your React Native version (the native Bunny SDK requires iOS 15+). The CocoaPods setup must support React Native's
spm_dependencyhelper. - Android: Android 8.0 (API 26)+,
compileSdk36+, JDK 17, Kotlin 2.2.20+, and core library desugaring. - A Bunny Stream video library with its library ID and API access key.
- A physical device for camera recording and broadcasting.
npm install @bunny.net/stream-react-nativeThe package links automatically. Android dependencies are resolved from Maven Central; iOS dependencies are resolved from the public Swift package.
iOS: install pods from your app's ios directory:
bundle exec pod installThe app must embed and sign GoogleInteractiveMediaAds.framework, a dependency of the native player. Add a Run Script build phase using the EMBED_FRAMEWORKS_SCRIPT from the iOS config plugin. The Expo plugin adds this automatically.
Android: use the SDK and Kotlin versions listed above, and enable desugaring in android/app/build.gradle:
android {
compileOptions {
coreLibraryDesugaringEnabled true
}
}
dependencies {
coreLibraryDesugaring "com.android.tools:desugar_jdk_libs:2.1.5"
}Ensure the app declares android.permission.INTERNET. For Picture-in-Picture, add android:supportsPictureInPicture="true" to the host activity.
Expo requires a development build with the New Architecture enabled. Expo Go and web are not supported.
Add the plugin to app.json:
{
"expo": {
"plugins": [
[
"@bunny.net/stream-react-native",
{
"cameraPermission": "Allow $(PRODUCT_NAME) to record and broadcast video.",
"microphonePermission": "Allow $(PRODUCT_NAME) to capture audio."
}
]
]
}
}Then generate and build the native app:
npx expo prebuild
npx expo run:ios
# or: npx expo run:androidThe plugin configures permissions, Android build settings and Picture-in-Picture, and iOS framework embedding and background audio. It runs during prebuild; apps that manage native projects manually must apply those settings themselves. See the plugin options for customization.
Before mounting the broadcaster, request camera and microphone permissions in your app. For manual native setup, also declare CAMERA and RECORD_AUDIO in AndroidManifest.xml, and add NSCameraUsageDescription and NSMicrophoneUsageDescription to Info.plist. The Expo plugin adds these declarations, but your app still needs to request runtime access.
Call initialize once during app startup, before mounting a player or using the API:
import { initialize } from '@bunny.net/stream-react-native';
initialize('your-library-api-key', 12345);import { BunnyStreamPlayer } from '@bunny.net/stream-react-native';
export function VideoScreen() {
return (
<BunnyStreamPlayer
source={{ type: 'vod', videoId: 'your-video-guid' }}
style={{ width: '100%', aspectRatio: 16 / 9 }}
/>
);
}For both VOD and live sources, source.libraryId is optional and falls back to the library passed to initialize(). Set it on the source to override the initialized library.
Set controls={false} to hide the player's built-in controls. See the player types for props, events, and commands.
Use the same component with a live source. It also uses the initialized library unless you explicitly provide libraryId:
<BunnyStreamPlayer
source={{ type: 'live', streamId: 'your-stream-guid' }}
style={{ width: '100%', aspectRatio: 16 / 9 }}
/>Missing or invalid library IDs report MISSING_LIBRARY_ID or INVALID_LIBRARY_ID through onError; without a handler, configuration errors throw. Native live errors use onLiveError.
After granting camera and microphone permissions, mount the broadcaster with an existing live stream. Its built-in controls start and stop the broadcast:
import { BunnyStreamBroadcaster } from '@bunny.net/stream-react-native';
<BunnyStreamBroadcaster
accessKey="your-library-api-key"
source={{ type: 'live', libraryId: 12345, streamId: 'your-stream-guid' }}
style={{ flex: 1 }}
onError={(event) => console.warn(event.message)}
/>;Use source={{ type: 'new', libraryId: 12345 }} to record and upload a new video instead. Broadcasting is foreground-only; stop it when the app enters the background.
API calls return a BunnyResult: check ok to access the value or error.
import { BunnyStreamApi } from '@bunny.net/stream-react-native';
const result = await BunnyStreamApi.listVideos(12345);
if (result.ok) {
console.log(result.value.items);
} else {
console.warn(result.error.message);
}The API reference in source also covers collections, live stream creation and scheduling, thumbnails, captions, reencoding, AI transcription, and analytics. See platform differences for capabilities that are not shared by both SDKs.
After initialization, pass a local file URI to start a resumable upload. The SDK creates the video entry for you:
import { BunnyStreamUpload } from '@bunny.net/stream-react-native';
const result = await BunnyStreamUpload.startUpload({
libraryId: 12345,
uri: 'file:///path/to/video.mp4',
title: 'My video',
mode: 'tus',
});
if (result.ok) {
console.log(result.value.uploadId);
} else {
console.warn(result.error.message);
}Subscribe with BunnyStreamUpload.addUploadListener to track progress and completion; call the returned unsubscribe function when finished. startUpload() defaults to basic, so set mode: 'tus' explicitly for resumable uploads. See the upload API for pause, resume, and cancellation.
Use continueUpload() to resume an interrupted TUS transfer. Do not assume uploads automatically retry when connectivity returns.
On iOS, TUS supports background uploads and reattaching to cached transfers with restoreUploads(); user force-quit cancels background transfers. On Android, uploads stop with the process; use continueUpload() with saved video/file details to resume after relaunch. Recovery depends on file availability, authorization, and server state.
Feature availability and options differ between platforms. See the player types and API source for operation-specific limitations. Android TV requires useNativeTvPlayer and the optional net.bunny:tv dependency.
See CONTRIBUTING.md for local setup, running the examples, and contribution guidelines.
Bunny Stream React Native is licensed under the MIT License.