Documentation Index
Fetch the complete documentation index at: https://mintlify.com/afkcodes/react-native-audio-pro/llms.txt
Use this file to discover all available pages before exploring further.
useAudioPro is a React hook that connects your components to the audio player’s internal Zustand store. Every field updates automatically as playback progresses — no manual event listeners or useEffect wiring required. The hook ships in two overloads: call it with no arguments to get the full state object, or pass a selector function to subscribe to only the slice your component actually needs.
import { useAudioPro } from 'react-native-audio-pro';
Overloads
Full state — useAudioPro()
useAudioPro(): UseAudioProReturn
Subscribes to all player state. The component re-renders whenever any field changes. Use this for player UIs that display multiple pieces of state simultaneously.
const { state, position, duration, playingTrack } = useAudioPro();
Selector — useAudioPro(selector, equalityFn?)
useAudioPro<T>(
selector: (state: AudioProStore) => T,
equalityFn?: (left: T, right: T) => boolean,
): T
Subscribe to a derived slice of state. The component only re-renders when the value returned by selector changes (using the optional equalityFn, or strict equality by default).
selector
(state: AudioProStore) => T
required
A function that receives the full store and returns the piece of state you care about.
equalityFn
(left: T, right: T) => boolean
Optional custom equality function. When it returns true, the component skips a re-render even if the selector ran. Useful for number fields where small changes don’t need to trigger a render (e.g. position with a tolerance).
Returns: T — the result of your selector.
// Only re-renders when the playing track changes
const track = useAudioPro((s) => s.trackPlaying);
Return Fields
When called without a selector, useAudioPro() returns a UseAudioProReturn object with the following fields:
Current playback state. One of 'IDLE', 'STOPPED', 'LOADING', 'PLAYING', 'PAUSED', or 'ERROR'.
Current playback position in milliseconds. Updated at the interval configured via AudioPro.configure({ progressIntervalMs }). Defaults to 0.
Total duration of the current track in milliseconds. 0 until the track is loaded. Defaults to 0.
How far ahead the player has buffered, in milliseconds. Useful for rendering a buffer indicator on a progress bar. Defaults to 0.
The track object that is currently loaded and playing. null when the queue is empty or the player is idle.
Zero-based index of the active track within the queue. -1 when no track is active.
Total number of tracks currently in the queue. 0 when the queue is empty.
Current playback speed multiplier. Range: 0.25 – 2.0. Normal speed is 1.0.
Current player volume. Range: 0.0 (mute) – 1.0 (full). Defaults to 1.0.
error
AudioProPlaybackErrorPayload | null
The most recent playback error, or null if the last operation succeeded. Cleared automatically when the state leaves ERROR. Contains error (message string), errorCode, recoverable, cause, and index.
Examples
Basic usage
Destructure all state fields in a single call. The component re-renders whenever any field changes.
import { useAudioPro } from 'react-native-audio-pro';
import { AudioPro } from 'react-native-audio-pro';
import { View, Text, Button } from 'react-native';
export function PlayerStatus() {
const {
state,
position,
duration,
playingTrack,
playbackSpeed,
volume,
error,
} = useAudioPro();
const isPlaying = state === 'PLAYING';
return (
<View>
<Text>{playingTrack?.title ?? 'Nothing playing'}</Text>
<Text>{playingTrack?.artist}</Text>
<Text>
{Math.floor(position / 1000)}s / {Math.floor(duration / 1000)}s
</Text>
<Text>State: {state}</Text>
<Button
title={isPlaying ? 'Pause' : 'Play'}
onPress={() => (isPlaying ? AudioPro.pause() : AudioPro.play())}
/>
{error && <Text>Error: {error.error}</Text>}
</View>
);
}
Pass a selector to subscribe to only one field. The component below only re-renders when the currently playing track changes — position ticks do not cause re-renders.
import { useAudioPro } from 'react-native-audio-pro';
import { Text, Image, View } from 'react-native';
export function NowPlayingTitle() {
// Re-renders only when the track object changes
const track = useAudioPro((s) => s.trackPlaying);
if (!track) return <Text>Nothing playing</Text>;
return (
<View>
<Image source={{ uri: track.artwork }} style={{ width: 56, height: 56 }} />
<Text>{track.title}</Text>
<Text>{track.artist}</Text>
</View>
);
}
Selector with tolerance equality
For high-frequency numeric fields like position, combine a selector with a custom equality function to throttle re-renders to meaningful visual changes.
import { useAudioPro } from 'react-native-audio-pro';
import { Slider } from '@react-native-community/slider';
import { AudioPro } from 'react-native-audio-pro';
// Only re-render when position changes by more than 500 ms
const POSITION_TOLERANCE_MS = 500;
export function ProgressSlider() {
const position = useAudioPro(
(s) => s.position,
(prev, next) => Math.abs(next - prev) < POSITION_TOLERANCE_MS,
);
const duration = useAudioPro((s) => s.duration);
return (
<Slider
value={position}
minimumValue={0}
maximumValue={duration || 1}
onSlidingComplete={(value) => AudioPro.seekTo(value)}
/>
);
}
Complete player component
A self-contained component that combines track info, a progress bar, play/pause, next/previous, and error handling — all reactive with no manual subscription management.
import { AudioPro, useAudioPro } from 'react-native-audio-pro';
import { View, Text, Button, StyleSheet } from 'react-native';
export function AudioPlayer() {
const {
state,
position,
duration,
bufferedPosition,
playingTrack,
activeTrackIndex,
queueSize,
error,
} = useAudioPro();
const isPlaying = state === 'PLAYING';
const isLoading = state === 'LOADING';
const progress = duration > 0 ? position / duration : 0;
const buffered = duration > 0 ? bufferedPosition / duration : 0;
const formatTime = (ms: number) => {
const s = Math.floor(ms / 1000);
return `${Math.floor(s / 60)}:${String(s % 60).padStart(2, '0')}`;
};
return (
<View style={styles.container}>
{/* Track info */}
<Text style={styles.title}>{playingTrack?.title ?? '—'}</Text>
<Text style={styles.artist}>{playingTrack?.artist}</Text>
{/* Progress */}
<Text>{formatTime(position)} / {formatTime(duration)}</Text>
<Text style={styles.queue}>
Track {activeTrackIndex + 1} of {queueSize}
</Text>
{/* Controls */}
<View style={styles.controls}>
<Button title="Prev" onPress={() => AudioPro.seekToPreviousMediaItem()} />
<Button
title={isLoading ? '…' : isPlaying ? 'Pause' : 'Play'}
onPress={() => (isPlaying ? AudioPro.pause() : AudioPro.play())}
/>
<Button title="Next" onPress={() => AudioPro.seekToNextMediaItem()} />
</View>
{/* Error */}
{error && (
<Text style={styles.error}>
{error.error} (code {error.errorCode ?? 'unknown'})
</Text>
)}
</View>
);
}
const styles = StyleSheet.create({
container: { padding: 16, gap: 8 },
title: { fontSize: 18, fontWeight: '600' },
artist: { fontSize: 14, color: '#888' },
queue: { fontSize: 12, color: '#aaa' },
controls: { flexDirection: 'row', gap: 16, justifyContent: 'center' },
error: { color: 'red', fontSize: 12 },
});
Subscription cleanup
useAudioPro uses Zustand internally and manages its own subscriptions — there is no need to wrap calls in useEffect or call any cleanup function. The subscription is automatically removed when the component unmounts.
// ✅ Correct — no cleanup needed
function TrackTitle() {
const title = useAudioPro((s) => s.trackPlaying?.title);
return <Text>{title ?? '—'}</Text>;
}
// ❌ Unnecessary — useAudioPro already handles this
function TrackTitleVerbose() {
const [title, setTitle] = useState<string | undefined>();
useEffect(() => {
const sub = AudioPro.addEventListener((event) => {
if (event.type === 'TRACK_CHANGED') {
setTitle(event.track?.title);
}
});
return () => sub.remove();
}, []);
return <Text>{title ?? '—'}</Text>;
}
Prefer selectors for leaf components. Components deep in the tree that only display a single field (track title, volume badge, queue size) should always use the selector overload so they don’t re-render on every position tick.
Combine related fields in one selector. If a component needs both position and duration together, select them as an object. Zustand’s shallow comparison will handle the rest: useAudioPro((s) => ({ position: s.position, duration: s.duration })). Without a custom equality function, use useShallow from zustand/shallow or provide your own comparator.
Use a tolerance equality function for sliders. Progress bars and sliders that map position to a visual pixel value don’t need sub-millisecond precision. A tolerance of 250 – 500 ms significantly reduces render frequency without any noticeable visual lag.
// Efficient pattern: select only what you render, skip trivial changes
const position = useAudioPro(
(s) => s.position,
(a, b) => Math.abs(a - b) < 250, // skip re-render if change < 250 ms
);
The no-argument overload useAudioPro() uses Zustand’s useShallow internally to prevent reference-equality false positives on the returned object. You do not need to wrap it in useShallow yourself.