Migrating from 0.x to 1.x
Version 1.0 brings the library closer to the Web Audio API specification, moves optional features out of the default build, and cleans up APIs that grew organically during the 0.x releases. This page lists everything you may need to change when upgrading from 0.13.x.
The guide is split into two parts:
- Installation and build — changes you hit before the app even starts.
- API changes — code that no longer compiles or no longer works.
If you use TypeScript, start by upgrading and running tsc. Most items in API changes surface as type errors. The renamed event handlers are the exception in plain JavaScript — assigning an old name silently creates an unused property.
Installation and build
FFmpeg is now opt-in
In 0.x, FFmpeg was linked by default and you could turn it off. In 1.x, it is off by default. Local files, assets and ArrayBuffers — including m4a, aac and mp4 — are now decoded with the platform decoders, so most apps no longer need FFmpeg at all.
You still need FFmpeg for:
- remote URL streaming with
createFileSourceor<Audio>(without it, a remote file is downloaded in full before playback starts, so a live stream never starts), - HLS (
.m3u8) sources, which throwNotSupportedErrorwithout FFmpeg, getAudioDurationon remote URLs and<Audio preload="metadata">.
The build options also moved. The Podfile environment variables and gradle.properties keys are no longer read — set the options in your app's package.json (or the Expo plugin) instead:
# Podfile
ENV['DISABLE_AUDIOAPI_FFMPEG'] = '1'
ENV['DISABLE_AUDIOAPI_STATIC_EXTERNAL_LIBS'] = '1'
# android/gradle.properties
disableAudioapiFFmpeg=true
disableAudioapiStaticExternalLibs=true
{
"react-native-audio-api": {
"enableFFmpeg": true,
"disableStaticExternalLibs": false
}
}
With Expo, replace disableFFmpeg with enableFFmpeg in the plugin options. The plugin writes the values into your package.json during expo prebuild, so commit that change.
["react-native-audio-api", {
"disableFFmpeg": true
"enableFFmpeg": true
}]
If you previously disabled the static external libraries (Opus, Vorbis) through the Podfile or gradle.properties, they are linked again until you move the option to package.json.
isFfmpegEnabled() keeps its name, but now returns false unless you enabled FFmpeg — including in the react-native-audio-api/mock implementation. See Prebuilt libraries support for details.
Worklets moved to react-native-audio-worklets
WorkletNode, WorkletProcessingNode and WorkletSourceNode are no longer part of react-native-audio-api, and the react-native-worklets auto-detection was removed from the native build. If you use worklets, install the new package:
npm install react-native-audio-worklets react-native-worklets
react-native-audio-worklets requires react-native-worklets >= 0.10.0 (0.x required >= 0.7.0) and the New Architecture. Code changes are described in Worklet nodes.
Deep imports no longer resolve
The package now declares an exports map. Only these entry points are public:
react-native-audio-apireact-native-audio-api/mockreact-native-audio-api/app.plugin.js(used automatically by the Expo plugin)
Imports such as react-native-audio-api/lib/... or react-native-audio-api/src/... fail in bundlers and tools that respect package exports (Metro, Jest, webpack, TypeScript with moduleResolution: "bundler"). Import everything from the package root.
Native build defaults
- A missing New Architecture flag now means the New Architecture is enabled. Apps on the legacy architecture must set
newArchEnabled=false(Android) orRCT_NEW_ARCH_ENABLED=0(iOS) explicitly. The React Native templates already do this. - The package declares
engines.node >= 22. - Swift Package Manager is supported as an alternative to CocoaPods — see Swift Package Manager. Nothing changes for CocoaPods users.
API changes
Event handlers use lowercase names
Event handler properties now follow the Web Audio API naming. The old camelCase names were removed without aliases.
| 0.x | 1.x |
|---|---|
onEnded | onended |
onLoopEnded | onloopended |
onBufferEnded | onbufferended |
onPositionChanged | onpositionchanged |
onPositionChangedInterval | onpositionchangedInterval |
source.onEnded = () => setPlaying(false);
source.onended = () => setPlaying(false);
Scheduled sources additionally support addEventListener('ended', ...) / removeEventListener('ended', ...), and the ended event carries type and target. Reassigning a handler now replaces the previous one; in 0.x the old callback kept firing.
<Audio> props (onEnded, onPositionChange, …), AudioRecorder callbacks and system events are not renamed.
Removed APIs
| Removed | Replacement |
|---|---|
StreamerNode, createStreamer(), StreamerOptions | createFileSource or <Audio> (remote streaming requires FFmpeg) |
RecorderAdapterNode, createRecorderAdapter() | recorder.connect(context, destination) |
createWorkletNode(), createWorkletProcessingNode(), createWorkletSourceNode(), AudioWorkletRuntime | react-native-audio-worklets |
AudioRecorderStartOptions | fileName in enableFileOutput |
SessionOptions type | SystemOptions |
BaseAudioBufferSourceOptions type | AudioBufferQueueSourceOptions |
AudioRecorder
Recorder graph connection
The recorder connects to the graph directly; the adapter node is created internally.
const adapter = ctx.createRecorderAdapter();
recorder.connect(adapter);
adapter.connect(ctx.destination);
recorder.connect(ctx, ctx.destination);
connect returns the destination node, so you can keep chaining. See Graph processing.
Recorder file output
start()no longer takes options. The file name moved toenableFileOutput, andfileNamePrefixwas removed.fileNameis used exactly as given (no timestamp appended) and overwrites an existing file. Without it, files are namedrecording_<timestamp>. Names must not contain.,/or\and are limited to 128 characters.enableFileOutputthrowsNotSupportedErrorinstead of returning an errorResult— when the format cannot be encoded on the platform, when options are invalid, or while recording.- Recording to
Cafon Android is no longer supported and throws.
const result = recorder.enableFileOutput({ fileNamePrefix: 'take' });
if (result.status === 'error') { /* ... */ }
await recorder.start({ fileNameOverride: 'take1' });
try {
recorder.enableFileOutput({ fileName: 'take1' });
} catch (e) { /* NotSupportedError */ }
await recorder.start();
Asynchronous pause, resume and disableFileOutput
These methods now return a Promise<void>. Calls are serialized with start() and stop(), and disableFileOutput() during a recording resolves once the current file is finalized.
recorder.pause();
await recorder.pause();
AudioManager and hooks
setAudioSessionOptions was renamed to setSystemOptions, and iOS options are nested under ios without the ios prefix. A new optional android section configures the Android communication session.
AudioManager.setAudioSessionOptions({
iosCategory: 'playAndRecord',
iosMode: 'default',
iosOptions: ['defaultToSpeaker'],
iosAllowHaptics: true,
});
AudioManager.setSystemOptions({
ios: {
category: 'playAndRecord',
mode: 'default',
options: ['defaultToSpeaker'],
allowHaptics: true,
},
});
In useAudioInput, onSelectInput was renamed to selectInput. It also accepts 'default' to hand input selection back to the system, as does AudioManager.setInputDevice.
const { onSelectInput } = useAudioInput();
const { selectInput } = useAudioInput();
AudioBufferQueueSourceNode
The node now has a proper playback state machine. Use resume() after pause() instead of calling start() again:
queue.pause();
queue.start();
queue.resume();
start()can be called only once and throwsInvalidStateErrorafterwards, also afterstop().- The default
offsetofstart(when, offset)changed from-1to0. pause()throwsInvalidStateErrorunless the node is playing;resume()throws unless it is paused.
Worklet nodes
Worklet nodes are classes imported from react-native-audio-worklets instead of factory methods on the context. Import react-native-audio-api before the worklets package.
import 'react-native-audio-api';
import { WorkletNode, WorkletProcessingNode } from 'react-native-audio-worklets';
const viz = ctx.createWorkletNode(
(audioData: Float32Array[], channelCount: number) => { 'worklet'; /* ... */ },
1024, 2, 'UIRuntime',
);
const viz = new WorkletNode(
ctx,
(audioData: Float32Array) => { 'worklet'; /* ... */ },
1024,
);
const fx = ctx.createWorkletProcessingNode(
(input, output, framesToProcess, currentTime) => { 'worklet'; /* ... */ },
'AudioRuntime',
);
const fx = new WorkletProcessingNode(
ctx,
(input, output, inputChannelCount, outputChannelCount, framesToProcess, currentTime) => {
'worklet';
/* ... */
},
);
WorkletNodealways runs on the UI runtime and receives a single down-mixed monoFloat32Array.bufferLengthmust be a power of two between 32 and 32768. A new'frequency-domain'mode is available.WorkletProcessingNodeandWorkletSourceNodealways run on the audio runtime, and their callbacks receive the channel counts beforeframesToProcess.
See Worklets for the full API, including the new WorkletAudioContext.