react-native-audio-recorder-player


π Version 4.1.0 Released with NitroModule Support!
β οΈ Important: Version 4.0.0 had issues with Nitro integration. Please install version 4.1.0 or later.
π΄ Critical for v4.x: Recording operations now run in background threads. You MUST implement loading states to handle the async delays, or your UI may appear unresponsive. See Component Examples for proper implementation.
This is a high-performance React Native module for audio recording and playback, now powered by NitroModules for direct native module access without bridge overhead. The library provides simple recorder and player functionalities for iOS, Android, and Web platforms with full TypeScript support and type safety.
Preview
Documentation & Resources
What's New in 4.0.0 π
NitroModule Migration
Version 4.0.0 introduces a complete rewrite using NitroModules, offering:
- Zero Bridge Overhead: Direct native module access for maximum performance
- Full Type Safety: TypeScript definitions generated from native specs
- Synchronous Methods: Where appropriate, for better developer experience
- Event Listeners: Native callbacks with type-safe event payloads
- Cross-Platform Code Generation: Automatic code generation for iOS (Swift) and Android (Kotlin)
- Background Processing: Recording operations now run in background threads to prevent UI blocking, requiring loading state management
- Web Platform Support: Full support for web browsers using Web Audio API and MediaRecorder API
Requirements
- React Native >= 0.73.0
- iOS >= 13.0
- Android minSdk >= 24
- Expo SDK >= 50 (for Expo users)
Migration from Older Versions
If you're upgrading from version 3.x or earlier, please refer to our Migration Guide for detailed instructions and breaking changes.
Getting started
β οΈ Important: Install version 4.1.0 or later to avoid Nitro integration issues from version 4.0.0.
Post Installation
After installing the packages, follow these steps:
-
iOS Setup:
cd ios && pod install
-
Android Setup:
No additional steps required. The module uses autolinking.
-
Web Setup:
For React Native Web, install the additional dependency:
yarn add react-native-web
Then configure your webpack to include the web-specific implementation:
module.exports = {
resolve: {
alias: {
'react-native': 'react-native-web'
}
}
};
Note: The nitro-codegen
command is already run during the library's build process. You don't need to run it in your application.
Platform-specific Configuration
iOS Configuration
-
Microphone Permission: Add to your Info.plist
:
<key>NSMicrophoneUsageDescription</key>
<string>Give $(PRODUCT_NAME) permission to use your microphone. Your record wont be shared without your permission.</string>
-
Minimum iOS Version: Ensure your minimum deployment target is iOS 13.0 or higher in your Podfile
:
platform :ios, '13.0'
Android Configuration
On Android you need to add permissions to AndroidManifest.xml
:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
Also, android above Marshmallow
needs runtime permission to record audio. Below are two approaches:
Minimal Approach (Recommended for Android 13+):
if (Platform.OS === 'android') {
try {
const granted = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.RECORD_AUDIO,
{
title: 'Audio Recording Permission',
message: 'This app needs access to your microphone to record audio.',
buttonNeutral: 'Ask Me Later',
buttonNegative: 'Cancel',
buttonPositive: 'OK',
}
);
if (granted === PermissionsAndroid.RESULTS.GRANTED) {
console.log('Recording permission granted');
} else {
console.log('Recording permission denied');
return;
}
} catch (err) {
console.warn(err);
return;
}
}
Full Permissions Approach (For older Android versions):
if (Platform.OS === 'android') {
try {
const grants = await PermissionsAndroid.requestMultiple([
PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE,
PermissionsAndroid.PERMISSIONS.READ_EXTERNAL_STORAGE,
PermissionsAndroid.PERMISSIONS.RECORD_AUDIO,
]);
if (
grants['android.permission.WRITE_EXTERNAL_STORAGE'] ===
PermissionsAndroid.RESULTS.GRANTED &&
grants['android.permission.READ_EXTERNAL_STORAGE'] ===
PermissionsAndroid.RESULTS.GRANTED &&
grants['android.permission.RECORD_AUDIO'] ===
PermissionsAndroid.RESULTS.GRANTED
) {
console.log('All permissions granted');
} else {
console.log('All required permissions not granted');
return;
}
} catch (err) {
console.warn(err);
return;
}
}
Methods
mmss | number seconds | string | Convert seconds to minute:second string |
mmssss | number seconds | string | Convert seconds to minute:second:millisecond string |
setSubscriptionDuration | number duration | void | Set callback interval in ms (default 500ms) |
startRecorder | string? uri, AudioSet? audioSet, | Promise<string> | Start recording with optional path and audio settings |
| boolean? meteringEnabled | | |
pauseRecorder | | Promise<string> | Pause recording |
resumeRecorder | | Promise<string> | Resume recording |
stopRecorder | | Promise<string> | Stop recording and return file path |
startPlayer | string? uri, Record<string, string>? headers | Promise<string> | Start playback with optional URI and HTTP headers |
stopPlayer | | Promise<string> | Stop playback |
pausePlayer | | Promise<string> | Pause playback |
resumePlayer | | Promise<string> | Resume playback |
seekToPlayer | number milliseconds | Promise<string> | Seek to position in milliseconds |
setVolume | number value | Promise<string> | Set volume (0.0 - 1.0) |
setPlaybackSpeed | number speed | Promise<string> | Set playback speed (0.5 - 2.0) |
addRecordBackListener | Function callback | void | Add recording progress listener |
removeRecordBackListener | | void | Remove recording progress listener |
addPlayBackListener | Function callback | void | Add playback progress listener |
removePlayBackListener | | void | Remove playback progress listener |
addPlaybackEndListener | Function callback | void | Add playback completion listener |
removePlaybackEndListener | | void | Remove playback completion listener |
Usage
Basic Usage
import AudioRecorderPlayer, {
AudioEncoderAndroidType,
AudioSourceAndroidType,
AVEncoderAudioQualityIOSType,
AVEncodingOption,
RecordBackType,
PlayBackType,
} from 'react-native-audio-recorder-player';
const onStartRecord = async () => {
AudioRecorderPlayer.addRecordBackListener((e: RecordBackType) => {
console.log('Recording progress:', e.currentPosition, e.currentMetering);
setRecordSecs(e.currentPosition);
setRecordTime(AudioRecorderPlayer.mmssss(Math.floor(e.currentPosition)));
});
const result = await AudioRecorderPlayer.startRecorder();
console.log('Recording started:', result);
};
const onStopRecord = async () => {
const result = await AudioRecorderPlayer.stopRecorder();
AudioRecorderPlayer.removeRecordBackListener();
console.log('Recording stopped:', result);
};
const onPauseRecord = async () => {
await AudioRecorderPlayer.pauseRecorder();
console.log('Recording paused');
};
const onResumeRecord = async () => {
await AudioRecorderPlayer.resumeRecorder();
console.log('Recording resumed');
};
const onStartPlay = async () => {
AudioRecorderPlayer.addPlayBackListener((e: PlayBackType) => {
console.log('Playback progress:', e.currentPosition, e.duration);
setCurrentPosition(e.currentPosition);
setTotalDuration(e.duration);
setPlayTime(AudioRecorderPlayer.mmssss(Math.floor(e.currentPosition)));
setDuration(AudioRecorderPlayer.mmssss(Math.floor(e.duration)));
});
AudioRecorderPlayer.addPlaybackEndListener((e: PlaybackEndType) => {
console.log('Playback completed:', e);
setIsPlaying(false);
setCurrentPosition(0);
});
const result = await AudioRecorderPlayer.startPlayer();
console.log('Playback started:', result);
};
const onPausePlay = async () => {
await AudioRecorderPlayer.pausePlayer();
};
const onStopPlay = async () => {
AudioRecorderPlayer.stopPlayer();
AudioRecorderPlayer.removePlayBackListener();
AudioRecorderPlayer.removePlaybackEndListener();
};
const seekTo = async (milliseconds: number) => {
await AudioRecorderPlayer.seekToPlayer(milliseconds);
};
const setVolume = async (volume: number) => {
await AudioRecorderPlayer.setVolume(volume);
};
const setSpeed = async (speed: number) => {
await AudioRecorderPlayer.setPlaybackSpeed(speed);
};
Audio Configuration
const audioSet: AudioSet = {
AVSampleRateKeyIOS: 44100,
AVFormatIDKeyIOS: AVEncodingOption.aac,
AVEncoderAudioQualityKeyIOS: AVEncoderAudioQualityIOSType.high,
AVNumberOfChannelsKeyIOS: 2,
AudioEncoderAndroid: AudioEncoderAndroidType.AAC,
AudioSourceAndroid: AudioSourceAndroidType.MIC,
};
const meteringEnabled = true;
const uri = await AudioRecorderPlayer.startRecorder(
undefined,
audioSet,
meteringEnabled
);
Default Path
- Default path for android uri is
{cacheDir}/sound.mp4
.
- Default path for ios uri is
{cacheDir}/sound.m4a
.
- Default path for web: Files are stored as Blob URLs in memory.
Tip: Store the file path returned by startRecorder()
immediately for later use in playback or file management.
Web Platform Support
Features
- Audio recording using MediaRecorder API
- Audio playback using Web Audio API
- Support for common audio formats (depends on browser)
- Real-time playback progress updates
- Volume and speed control
Limitations
- Recording format is browser-dependent (typically webm/opus)
- Some audio configuration options are not supported
- File paths are Blob URLs instead of file system paths
- Metering during recording is not currently supported
Browser Compatibility
- Chrome/Edge: Full support
- Firefox: Full support
- Safari: Limited recording format support (may require polyfills)
Component-Based Implementation
For better code organization, consider separating recording and playback into separate components:
Important: Loading States
Note: Starting from version 4.x, recording operations (start/stop) are processed in the background to prevent UI blocking. This means there's a slight delay between calling the method and the actual operation completing. We strongly recommend implementing loading states to provide better user experience.
AudioRecorder Component with Loading States
import React, { useState } from 'react';
import { View, Button, Text, ActivityIndicator } from 'react-native';
import AudioRecorderPlayer from 'react-native-audio-recorder-player';
export const AudioRecorder = ({ onRecordingComplete }) => {
const [isRecording, setIsRecording] = useState(false);
const [isLoading, setIsLoading] = useState(false);
const [recordTime, setRecordTime] = useState('00:00:00');
const onStartRecord = async () => {
setIsLoading(true);
try {
const result = await AudioRecorderPlayer.startRecorder();
AudioRecorderPlayer.addRecordBackListener((e) => {
setRecordTime(AudioRecorderPlayer.mmssss(Math.floor(e.currentPosition)));
});
setIsRecording(true);
} catch (error) {
console.error('Failed to start recording:', error);
} finally {
setIsLoading(false);
}
};
const onStopRecord = async () => {
setIsLoading(true);
try {
const result = await AudioRecorderPlayer.stopRecorder();
AudioRecorderPlayer.removeRecordBackListener();
setIsRecording(false);
onRecordingComplete?.(result);
} catch (error) {
console.error('Failed to stop recording:', error);
} finally {
setIsLoading(false);
}
};
return (
<View>
<Text>{recordTime}</Text>
<Button
title={isRecording ? 'Stop Recording' : 'Start Recording'}
onPress={isRecording ? onStopRecord : onStartRecord}
disabled={isLoading}
/>
{isLoading && <ActivityIndicator />}
</View>
);
};
AudioPlayer Component with Loading States
import React, { useState } from 'react';
import { View, Button, Text, ActivityIndicator } from 'react-native';
import AudioRecorderPlayer from 'react-native-audio-recorder-player';
export const AudioPlayer = ({ audioPath }) => {
const [isPlaying, setIsPlaying] = useState(false);
const [isLoading, setIsLoading] = useState(false);
const [playTime, setPlayTime] = useState('00:00:00');
const [duration, setDuration] = useState('00:00:00');
const onStartPlay = async () => {
setIsLoading(true);
try {
const msg = await AudioRecorderPlayer.startPlayer(audioPath);
AudioRecorderPlayer.addPlayBackListener((e) => {
setPlayTime(AudioRecorderPlayer.mmssss(Math.floor(e.currentPosition)));
setDuration(AudioRecorderPlayer.mmssss(Math.floor(e.duration)));
});
AudioRecorderPlayer.addPlaybackEndListener((e) => {
console.log('Playback completed', e);
setIsPlaying(false);
setPlayTime('00:00:00');
});
setIsPlaying(true);
} catch (error) {
console.error('Failed to start playback:', error);
} finally {
setIsLoading(false);
}
};
const onStopPlay = async () => {
setIsLoading(true);
try {
await AudioRecorderPlayer.stopPlayer();
AudioRecorderPlayer.removePlayBackListener();
AudioRecorderPlayer.removePlaybackEndListener();
setIsPlaying(false);
setPlayTime('00:00:00');
setDuration('00:00:00');
} catch (error) {
console.error('Failed to stop playback:', error);
} finally {
setIsLoading(false);
}
};
return (
<View>
<Text>{playTime} / {duration}</Text>
<Button
title={isPlaying ? 'Stop' : 'Play'}
onPress={isPlaying ? onStopPlay : onStartPlay}
disabled={!audioPath || isLoading}
/>
{isLoading && <ActivityIndicator />}
</View>
);
};
Example App
Running the Example
Troubleshooting
iOS Recording Error: "Unknown std::runtime_error"
If you encounter this error when trying to record on iOS:
-
Ensure microphone permissions are properly configured in your Info.plist
:
<key>NSMicrophoneUsageDescription</key>
<string>Your app needs microphone access to record audio</string>
-
Clean and rebuild your iOS project:
cd ios
rm -rf build Pods
pod install
cd ..
yarn ios
-
Make sure you're testing on a real device if using the simulator doesn't work. Some audio features require real hardware.
-
Verify the Nitro modules are properly linked by checking that the [NitroModules] π₯ AudioRecorderPlayer is boosted by nitro!
message appears during pod install
.
Common Issues
- "nitro-codegen" command not found: This command is only needed when developing the library itself, not when using it in your app.
- Module not found errors: Make sure to run
pod install
after installing the package.
- Android build issues: Ensure your
minSdkVersion
is 24 or higher in android/build.gradle
.
Clean Build Instructions
If you're experiencing build issues or runtime errors after updating the library:
iOS Clean Build
cd ios
rm -rf ~/Library/Caches/CocoaPods
rm -rf Pods
rm -rf ~/Library/Developer/Xcode/DerivedData/*
pod cache clean --all
pod install
cd ..
Then in Xcode:
- Product β Clean Build Folder (β§βK)
- Product β Build (βB)
Android Clean Build
cd android
./gradlew clean
rm -rf ~/.gradle/caches/
cd ..
Then rebuild:
yarn android
npx react-native run-android
Both Platforms
You can also try resetting Metro cache:
npx react-native start --reset-cache
Contributing
See the contributing guide to learn how to contribute to the repository and the development workflow.
License
MIT
Made with create-react-native-library