React provider component that manages audio playback lifecycle, state synchronization, and error handling.
The AudioProvider component is the core of the audio system. It manages the HTML audio element lifecycle, synchronizes playback state with the Zustand store, handles errors with automatic retries, and preloads tracks for seamless playback.
Installation
pnpm dlx shadcn@latest add @audio/provider
Setup
Wrap your app with AudioProvider at the root level. This provides the audio context to all components.
// app/layout.tsx or _app.tsx
import { AudioProvider } from "@/components/audio/provider";
export default function RootLayout({ children }) {
return (
<html>
<body>
<AudioProvider tracks={initialTracks}>{children}</AudioProvider>
</body>
</html>
);
}Required: The AudioProvider must wrap all components
that use audio functionality. It initializes the audio singleton, manages event
listeners, and synchronizes state between the HTML audio element and the Zustand
store.
Usage
Basic
import { AudioProvider } from "@/components/audio/provider";
const tracks = [
{
id: "1",
title: "Track 1",
artist: "Artist 1",
url: "https://example.com/track1.mp3",
},
];
function App() {
return (
<AudioProvider tracks={tracks}>
<YourAudioComponents />
</AudioProvider>
);
}With Dynamic Tracks
import { AudioProvider } from "@/components/audio/provider";
import { useState } from "react";
function App() {
const [tracks, setTracks] = useState([]);
return (
<AudioProvider tracks={tracks}>
<YourAudioComponents />
</AudioProvider>
);
}API Reference
AudioProvider
Props
| Prop | Type | Default | Description |
|---|---|---|---|
tracks | Track[] | [] | Initial array of tracks to populate the queue. |
children | ReactNode | - | Child components that will have access to the audio context. |
Features
- Audio Element Management: Initializes and manages the HTML audio element lifecycle
- State Synchronization: Keeps Zustand store in sync with audio element state
- Error Handling: Automatic retry logic (up to 3 attempts with exponential backoff)
- Track Preloading: Preloads the next track in the queue for seamless playback
- Event Handling: Listens to all audio events (play, pause, error, ended, timeupdate, etc.)
- State Restoration: Restores playback state from localStorage on mount
- Live Stream Support: Handles live streams with appropriate timeouts and seeking restrictions
Architecture
How It Works
Initialization
- Audio Singleton: Uses
useAudio()hook to access thehtmlAudiosingleton - Audio Element: Calls
htmlAudio.init()to ensure the audio element is created - Event Listeners: Attaches event listeners to the audio element using
AbortControllerfor cleanup - State Subscriptions: Subscribes to Zustand store changes for
isPlaying,currentTrack,volume, etc.
State Synchronization
The provider maintains bidirectional synchronization:
- Store → Audio: When store state changes (e.g.,
isPlaying,volume), the provider updates the audio element - Audio → Store: When audio events fire (e.g.,
play,pause,timeupdate), the provider updates the store
Error Handling
The provider implements automatic error recovery:
- Error Detection: Listens to
errorevents from the audio element - Error Classification: Determines if the error is recoverable (network issues) or fatal (decoding errors)
- Retry Logic: Automatically retries up to 3 times with exponential backoff (1s, 2s, 4s)
- Error State: Updates store with error information if all retries fail
Track Preloading
To ensure seamless playback:
- Next Track Calculation: Calculates the next track based on queue, shuffle, and repeat mode
- Background Loading: Preloads the next track in a separate audio element
- Automatic Cleanup: Clears preload when queue changes or component unmounts
Examples
With Player Components
import { AudioProvider } from "@/components/audio/provider";
import { AudioPlayer, AudioPlayerPlay } from "@/components/audio/player";
const tracks = [
{ id: "1", title: "Track 1", url: "https://example.com/track1.mp3" },
];
function App() {
return (
<AudioProvider tracks={tracks}>
<AudioPlayer>
<AudioPlayerPlay />
</AudioPlayer>
</AudioProvider>
);
}With Queue Management
import { AudioProvider } from "@/components/audio/provider";
import { AudioQueue } from "@/components/audio/queue";
function App() {
return (
<AudioProvider tracks={tracks}>
<AudioQueue />
</AudioProvider>
);
}Notes
- Singleton Pattern: The provider uses the
htmlAudiosingleton fromuseAudio()hook. This ensures a single audio element is shared across the entire app. - Event Cleanup: Uses
AbortControllerto automatically clean up all event listeners when the component unmounts. - State Restoration: Automatically restores playback state (current track, position, volume) from localStorage on mount.
- Throttled Updates: Time updates are throttled to 100ms to prevent excessive re-renders while maintaining smooth UI updates.
- Live Stream Detection: Automatically detects live streams based on duration (NaN, Infinity) and adjusts behavior accordingly.
Error Handling: The provider implements silent error handling. All errors are handled internally with retry logic and state updates. No console errors are logged unless explicitly configured.
Related
- Audio Player — Player UI components
- Audio Store — Zustand store for state management
- Audio Library — Core audio singleton
- useAudio Hook — React hook for audio access
Changelog
2025-12-24 Provider refactoring and useAudio integration
- Added: Integration with
useAudio()hook for accessing audio singletons - Changed: Replaced direct
$audiousage withhtmlAudiofromuseAudio()hook - Improved: Event listener management using
AbortControllerfor cleaner cleanup - Improved: State synchronization logic for better reliability
- Fixed: Slider synchronization when pausing and resuming playback
- Removed: All console error logging (silent error handling)
- Improved: React Compiler optimization compatibility
Last updated 12/24/2025
On This Page