react-native-incall-manager: call audio routing for React Native apps
Handling media-routes/sensors/events during a audio/video chat on React Native
At a glance
- What is it?
- react-native-incall-manager handles the parts of a phone call that a WebRTC stack does not: routing audio to the right output when a headset is plugged in, reacting to the proximity sensor, playing ringtone, ringback and busy tones, and toggling the speaker, microphone or flashlight. It is written in Java and lives in the react-native-webrtc organisation.
- Who is it for?
- react-native-incall-manager suits a React Native team building a voice or video calling app that has to behave like a real call, particularly on Android where the headset and proximity sensor behaviour is not exposed to JavaScript.
- Can I use it commercially?
- Yes. ISC is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 38 days ago.
- What is it written in?
- Mainly Java, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.
Editorial analysis
What a WebRTC library does not handle
react-native-incall-manager is a module for handling media routes, sensors and events during an audio or video chat on React Native, and its purpose section describes it as handling actions and events during a phone call.
The specific list is what makes the gap obvious. It manages device events such as the wired headset plugged-in state and proximity sensors, and exposes those functionalities to JavaScript. It automatically routes audio to the proper devices based on those events and on the platform API. It can toggle the speaker or the microphone on and off, toggle the flashlight on and off, and play a ringtone, a ringback or a DTMF tone.
The author's summary is that it is basically a telecommunication module handling most of the requirements when making, receiving or talking on a call. That is the honest framing. A WebRTC library gives you an audio track and a peer connection; it does not know that the user just plugged in headphones and that the audio should move, or that the phone is against the ear and the screen should be off, or that a call failed and the other side should hear a busy tone.
The module is designed to work with react-native-webrtc, and it now lives in that project's GitHub organisation, which is how you find it, since the npm install instructions in the readme point at a git URL under a different account name.
The top-level listing shows the shape of it: an android/ directory and an ios/ directory, a ReactNativeIncallManager.podspec for CocoaPods, an index.js and an index.d.ts for the JavaScript surface, and a test/ directory.
The API is a start and a stop, with tones attached
The usage section is short because the module implements a basic handle logic automatically, and the readme shows the whole lifecycle.
import InCallManager from 'react-native-incall-manager';
// --- start manager when the chat start based on logics of your app
// On Call Established:
InCallManager.start({media: 'audio'}); // audio/video, default: audio
// ... it will also register and emit events ...
// --- On Call Hangup:
InCallManager.stop();
// ... it will also remove event listeners ...So start is called when the call is established and stop when it is hung up, and the module registers its event listeners on start and removes them on stop. The media option is audio or video, defaulting to audio, which is the setting that decides whether the microphone routing follows the call or the camera as well.
The tones are separate concerns with their own rules, and the distinction between them is the part to get right.
Ringback is for an outgoing call and is part of start(). You can ask for it by name, with _BUNDLE_ for the bundled file, _DEFAULT_ for the system sound or _DTMF_ for the tone. When the callee answers you must stop it explicitly:
InCallManager.start({media: 'audio', ringback: '_BUNDLE_'}); // or _DEFAULT_ or _DTMF_
//when callee answered, you MUST stop ringback explicitly:
InCallManager.stopRingback();Busy tone is also for an outgoing call but belongs to stop(), and it is what you play when the call failed or the callee is busy:
InCallManager.stop({busytone: '_DTMF_'}); // or _BUNDLE_ or _DEFAULT_Ringtone is for an incoming call and is independent of start and stop, so you can play it before the user picks up:
InCallManager.startRingtone('_BUNDLE_'); // oThat last example is truncated in the readme itself, but the shape of it is the same as the other two, and the three tones are the whole sound surface of the module.
Android needs a manual link and API level 24
The Android installation section is the one with the most moving parts, and it starts with a hard requirement.
The module requires Android 7.0, API level 24, or later, and you set minSdkVersion to at least 24 in your app's android/app/build.gradle. There is a note that you might need the android.permission.BLUETOOTH permission for Bluetooth to work, which matters because the headset routing this module exists to handle is frequently a Bluetooth one.
After installing you can use rnpm, installed with npm install rnpm -g, to link the Android side, or use react-native link react-native-incall-manager, or do it manually. There is also a note about the Android support library v4 being used to check and request permissions, with an instruction to add the support-v4 compile dependency in your app's build.gradle.
The manual instructions are given in three places, and they cover the Gradle file, the settings file and the Java application class. In android/app/build.gradle you need a line with compile(project(':react-native-incall-manager')) in the dependencies section. In android/settings.gradle you need two lines:
include ':react-native-incall-manager'
project(':react-native-incall-manager').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-incall-manager/android')And in MainApplication.java the package has to be added to the list of ReactPackage instances:
import com.zxcpoiu.incallmanager.InCallManagerPackage;The import path carries the original author's account name, which is a small detail that matters if you are searching the codebase for the module's Java sources and expect to find them under the react-native-webrtc organisation instead.
There is also a set of optional sound files. If you want the bundled ringtone, ringback or busy tone rather than the system sound, you put files in android/app/src/main/res/raw named incallmanager_ringtone.mp3, incallmanager_ringback.mp3 and incallmanager_busytone.mp3, and as long as the file extension is supported by Android the module will load it.
iOS is CocoaPods or a manual Xcode project dance
The iOS side is shorter to install but has a documented failure mode.
The first step is react-native link react-native-incall-manager, and for CocoaPods you update the Podfile line with your path to node_modules and add it, using the pod entry for ReactNativeIncallManager with a path pointing at ../node_modules/react-native-incall-manager.
If the link does not work, the manual route is listed in four moves. You drag the RNInCallManager.xcodeproj under your Xcode project in the Libraries folder. You select your project, then Build Phases, then Link Binary With Libraries, and drag the libRNInCallManager.a product into that phase. You select your project again, then Build Settings, and in the Header Search Paths you add the path into the ios directory of the module inside node_modules.
The section that follows is called cleaning the project if messed up, and it is the most honest part of the readme. The installation steps are a bit complex, and it might be related to the Xcode version, the Xcode cache, converting the Swift version, and your own path configurations. If something is messed up, the recovery is: delete all project and directory entries in Xcode related to incall-manager, delete the module in node_modules, run Product then clean in Xcode, close Xcode, run npm install again, and open Xcode and try the install process again step by step. The readme closes that section by asking anyone who knows a simpler way to set the project up to say so.
That is a real cost to plan for. The iOS instructions are not the single command they are on Android, and the failure recovery involves uninstalling and reinstalling.
The iOS sound files follow the same naming scheme as Android, with incallmanager_ringtone.mp3, incallmanager_ringback.mp3 and incallmanager_busytone.mp3 added into the project under the xcodeproject root, with copy file if needed checked, and on iOS only mp3 files are supported.
The maintainers' own list of what is unfinished
The repository publishes a TODO list under the heading TODO and Contribution Wanted, and it is worth reading before you adopt the module because it tells you exactly where the edges are.
The first item is to make operations run on the main thread, on both iOS and Android. The second is to fix the iOS audio shared instance singleton conflict with internal webrtc. The third is to detect hardware button press events and react to them, with the examples given being the Bluetooth button to send an event to JavaScript to answer or hang up, and the power button to mute an incoming ringtone. The fourth is to use a config-based approach to decide which event should start and report, maybe controlling behaviour as well. The fifth is a Flash API on Android.
The second item deserves attention. A conflict with the internal webrtc library over a shared audio instance is not a cosmetic issue, it is the kind of bug where two components both believe they own the audio session. The first item is about threading, and on a native module that means the operations currently run off the main thread, which is where a surprising number of platform specific crashes come from.
The hardware button item is the interesting one for the stated purpose of the module. A user pressing the Bluetooth button to answer a call is standard behaviour, and the module is not yet able to do it.
The maintenance facts around all this are reasonable. The repository is not archived, the default branch is master, and the last push was on 2026-09-15, five weeks before the current date. The three most recent releases are 4.3.0 from 2026-09-15, 4.2.2 from 2026-07-24 and 4.2.1 from 2025-04-30. The package is ISC licensed, with a single peer dependency on react-native at version 0.40.0 or newer, and one test script that runs node test/index.test.js.
Editorial conclusion
react-native-incall-manager suits a React Native team building a voice or video calling app that has to behave like a real call, particularly on Android where the headset and proximity sensor behaviour is not exposed to JavaScript. It does not suit a team that only needs to capture and play audio, since the WebRTC library does that, and the installation is manual on both platforms, with the iOS path documented as a multi step process with a clean and start again procedure when it goes wrong. Before adopting it, read the module's own TODO list, which still lists running operations on the main thread and fixing an iOS audio shared instance conflict with the internal webrtc library, and test the headset and proximity behaviour on real devices.
Frequently asked questions
What does react-native-incall-manager handle?
It handles actions and events during a phone call on React Native: managing device events such as the wired headset plugged-in state and proximity sensors and exposing them to JavaScript, automatically routing audio to the proper devices based on those events and the platform API, toggling the speaker, microphone or flashlight, and playing a ringtone, ringback or DTMF tone.
How do I start and stop the in-call manager?
Call InCallManager.start with a media option of audio or video, defaulting to audio, when the call is established, and InCallManager.stop when it is hung up. The module registers and emits its events on start and removes the event listeners on stop.
What is the difference between ringback, busytone and ringtone?
Ringback is for an outgoing call and is part of start, and it must be stopped explicitly with stopRingback once the callee answers. Busytone is also for an outgoing call but is part of stop, for when the call failed. Ringtone is for an incoming call and is independent of start and stop, so it can be played before the user picks up.
What are the Android requirements for react-native-incall-manager?
Android 7.0, API level 24, or later, with minSdkVersion set to at least 24 in the app's android/app/build.gradle, and possibly the android.permission.BLUETOOTH permission for Bluetooth headset routing to work.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/react-native-webrtc-react-native-incall-manager)Was this useful?