6.4 KiB
6.4 KiB
Music Player Implementation Options
Option 1: Using react-player Library (RECOMMENDED) ✅
Installation
bun add react-player
Benefits
- ✅ Battle-tested - Used in production by thousands of apps
- ✅ Handles all edge cases - Browser differences, loading states, etc.
- ✅ Simple API - Easy to use and maintain
- ✅ Supports multiple formats - MP3, WAV, OGG, YouTube, Vimeo, etc.
- ✅ Built-in progress handling - No manual interval management
- ✅ Seek works perfectly - No browser compatibility issues
Usage Example
import { MusicPlayer } from './lib/MusicPlayer';
function MyComponent() {
return (
<MusicPlayer
url="https://example.com/song.mp3"
playing={true}
volume={0.7}
onEnded={() => console.log('Song ended')}
/>
);
}
Files Created
MusicPlayer.tsx- Wrapper component using react-player- Handles all audio logic internally
- Progress bar with seek functionality
- Play/pause controls
Option 2: Custom Hook useAudioPlayer
When to Use
- Need full control over audio element
- Want to avoid external dependencies
- Custom requirements not supported by libraries
Files Created
use-audio-player.ts- Custom React hookSimpleMusicPlayer.tsx- Example component
Usage
import { useAudioPlayer } from './lib/use-audio-player';
function MyComponent() {
const {
isPlaying,
currentTime,
duration,
play,
pause,
seek,
} = useAudioPlayer({ src: '/path/to/audio.mp3' });
return (
<div>
<button onClick={isPlaying ? pause : play}>
{isPlaying ? 'Pause' : 'Play'}
</button>
<input
type="range"
min="0"
max={duration}
value={currentTime}
onChange={(e) => seek(Number(e.target.value))}
/>
</div>
);
}
Option 3: Original Implementation (FIXED)
Current Status
- ✅ Working with Pause→Seek→Play pattern
- ✅ hasSeeked flag prevents reset
- ✅ Retry logic with load()
- ⚠️ Complex, hard to maintain
- ⚠️ Multiple edge cases to handle
When to Keep
- Already invested time in custom implementation
- Need specific customizations
- Don't want external dependencies
Recommendation
🎯 USE OPTION 1: react-player
Why?
- Less code - 100+ lines saved
- More reliable - Battle-tested library
- Easier maintenance - Library handles updates
- Better browser support - Handles cross-browser issues
- More features - Supports video, YouTube, Vimeo, etc.
Migration Steps:
- Install:
bun add react-player - Import:
import MusicPlayer from './lib/MusicPlayer' - Replace existing player component
- Done!
Comparison
| Feature | react-player | Custom Hook | Original |
|---|---|---|---|
| Lines of Code | ~50 | ~100 | ~300 |
| Browser Support | ✅ Excellent | ⚠️ Manual | ⚠️ Manual |
| Seek Functionality | ✅ Perfect | ✅ Good | ⚠️ Complex |
| Progress Updates | ✅ Built-in | ✅ Manual | ✅ Manual |
| Format Support | ✅ Many | ⚠️ Limited | ⚠️ Limited |
| Maintenance | ✅ Library | ⚠️ You | ⚠️ You |
| Bundle Size | +15kb | +0kb | +0kb |
Implementation with react-player
Basic Player
import ReactPlayer from 'react-player';
function BasicPlayer() {
return (
<ReactPlayer
url="https://example.com/song.mp3"
playing={true}
controls={true}
/>
);
}
Custom Player with Progress
import ReactPlayer from 'react-player';
import { useState } from 'react';
function CustomPlayer() {
const [played, setPlayed] = useState(0);
return (
<>
<ReactPlayer
url="https://example.com/song.mp3"
onProgress={(e) => setPlayed(e.played)}
/>
<input
type="range"
min="0"
max="1"
value={played}
onChange={(e) => playerRef.current?.seekTo(parseFloat(e.target.value))}
/>
</>
);
}
Advanced Player with All Controls
import ReactPlayer from 'react-player';
import { useRef, useState } from 'react';
function AdvancedPlayer({ url }) {
const playerRef = useRef(null);
const [playing, setPlaying] = useState(false);
const [volume, setVolume] = useState(0.5);
const [muted, setMuted] = useState(false);
const [played, setPlayed] = useState(0);
const [duration, setDuration] = useState(0);
return (
<div>
<ReactPlayer
ref={playerRef}
url={url}
playing={playing}
volume={volume}
muted={muted}
onProgress={(e) => setPlayed(e.played)}
onDuration={setDuration}
onEnded={() => setPlaying(false)}
/>
{/* Progress Bar */}
<input
type="range"
min="0"
max="1"
value={played}
onChange={(e) => playerRef.current?.seekTo(parseFloat(e.target.value))}
/>
{/* Controls */}
<button onClick={() => setPlaying(!playing)}>
{playing ? 'Pause' : 'Play'}
</button>
<button onClick={() => setMuted(!muted)}>
{muted ? 'Unmute' : 'Mute'}
</button>
<input
type="range"
min="0"
max="1"
step="0.01"
value={volume}
onChange={(e) => setVolume(parseFloat(e.target.value))}
/>
</div>
);
}
Next Steps
If Using react-player:
- ✅ Already installed
- Use
MusicPlayer.tsxcomponent - Or create custom wrapper for your needs
- Remove old complex logic
If Keeping Custom Implementation:
- Keep current files
- Test thoroughly
- Handle edge cases manually
- Maintain browser compatibility
Additional Libraries (Alternatives)
1. howler.js
- Great for audio sprites
- Good for games
- More low-level control
2. wavesurfer.js
- Waveform visualization
- Audio editing features
- More complex use cases
3. use-sound
- React hook for sound effects
- Simple API
- Built on howler.js
Conclusion
For your use case (Desa Darmasaba music player):
✅ USE react-player because:
- Simple integration
- Reliable seek functionality
- Less code to maintain
- Better browser support
- Already installed!
Files to use:
MusicPlayer.tsx- Base component- Customize as needed
- Remove old complex implementation
Updated: February 27, 2026
Recommendation: Use react-player library
Status: ✅ Installed and ready to use