Building a Custom Audio Player in React: A Step-by-Step Guide
Nilesh Kumar
Posted on November 7, 2024
Introduction: In this blog, we’re building a simple yet powerful audio player component in React. With the ability to play, pause, stop, and skip audio, this component leverages React hooks and interactive icons to create a seamless audio experience. Let's dive into each part and learn how to integrate this audio player in any project that uses audio in base64 format.
Component Breakdown
This PlayStory
component makes use of React Icons
for play, pause, skip, and stop buttons. It also handles audio state and playback using useState
and useEffect
hooks.
import {
IoPlayCircleOutline,
IoPauseCircleOutline,
IoStopCircleOutline,
IoPlaySkipBackCircleOutline,
IoPlaySkipForwardCircleOutline,
} from "react-icons/io5";
These imported icons give the player a clean, accessible design. Let's go over each major part:
Install react-icon from npm store
npm i react-icons
1. Component Initialization
The PlayStory component receives an audio prop in base64 format. This is converted into a playable audio file inside the useEffect hook. The playing state determines whether the audio is currently playing or paused, while duration and currentTime handle the audio’s total time and current time display.
Key states:
- playing: Boolean, toggles audio play and pause.
- audioFile: Holds the created audio file object.
- duration: Stores formatted total duration of the audio.
- currentTime: Stores formatted time of the current playback position.
2. Formatting Audio Time
The formatTime
function converts raw audio seconds into an "MM
" format. This is used to show duration
and currentTime
for a better user experience.
const formatTime = (seconds) => {
const minutes = Math.floor(seconds / 60);
const remainingSeconds = Math.floor(seconds % 60);
return `${minutes.toString().padStart(2, "0")}:${remainingSeconds.toString().padStart(2, "0")}`;
};
3. Audio Controls
This component comes with essential playback controls:
Play/Pause Toggle
ThehandlePlayPause
function toggles between playing and pausing the audio based on theplaying
state. When paused, it stops audio playback; when played, it resumes.Stop
ThehandleStop
function stops the audio, setscurrentTime
to the beginning, and resets theplaying
state.Skip Back/Forward
ThehandleSkipBack
andhandleSkipForward
functions allow users to jump back or forward by 10 seconds in the audio.
const handlePlayPause = () => {
if (audioFile) {
if (playing) {
audioFile.pause();
} else {
audioFile.play();
}
isPlaying((pre) => !pre);
}
};
const handleStop = () => {
audioFile.pause();
audioFile.currentTime = 0;
isPlaying(false);
};
const handleSkipBack = () => {
const newTime = audioFile.currentTime - 10;
audioFile.currentTime = newTime < 0 ? 0 : newTime;
};
const handleSkipForward = () => {
const newTime = audioFile.currentTime + 10;
audioFile.currentTime =
newTime > audioFile.duration ? audioFile.duration : newTime;
};
4. State to be manage for player
const { base64audio } = props; // get the audio data in base64
const [playing, isPlaying] = useState(false);
const [audioFile, setAudioFile] = useState(null);
const [duration, setDuration] = useState("00:00");
const [currentTime, setCurrentTime] = useState("00:00");
5. Managing Audio Playback with Hooks
Two useEffect
hooks are used for:
- Setting up the audio file from the base64 prop, and setting
duration
when audio is loaded. - Updating
currentTime
while the audio plays, and resettingplaying
state when the audio ends.
useEffect(() => {
if (base64adio) {
const audio = new Audio(`data:audio/x-wav;base64, ${base64adio}`);
setAudioFile(audio);
audio.onloadeddata = () => {
const audioDuration = audio.duration;
if (typeof audioDuration === "number" && !isNaN(audioDuration)) {
setDuration(formatTime(audioDuration));
}
};
}
}, [base64adio]);
6. Update current time while audio is playing
adding the event to manage current time
useEffect(() => {
if (audioFile) {
// // Update current time while audio is playing
audioFile.ontimeupdate = () => {
const duration = audioFile.duration;
const currentTime = audioFile.currentTime;
if (duration === currentTime) {
isPlaying(false);
audioFile.currentTime = 0;
audioFile.pause();
setCurrentTime(formatTime(audioFile.currentTime));
} else {
setCurrentTime(formatTime(currentTime));
}
};
}
7. Rendering the Component
The component’s return function includes:
-
currentTime
andduration
display. - Icon buttons for
skip back
,play/pause
,stop
, andskip forward
. Each icon is styled and sized for a better look and user interaction.
return (
<div className="flex items-center justify-between mx-1">
{currentTime}
<IoPlaySkipBackCircleOutline onClick={handleSkipBack} size={25} />
{playing ? (
<IoPauseCircleOutline onClick={handlePlayPause} size={25} />
) : (
<IoPlayCircleOutline onClick={handlePlayPause} size={25} />
)}
<IoStopCircleOutline onClick={handleStop} size={25} />
<IoPlaySkipForwardCircleOutline onClick={handleSkipForward} size={25} />
{duration}
</div>
);
Usage Example
To integrate this audio player in your project, you only need to pass the audio data in base64 format to the any component. The player will handle playback controls seamlessly, providing an easy-to-use audio experience.
Complete code
Here is the complete working code.
import React, { useEffect, useState } from "react";
import {
IoPlayCircleOutline,
IoPauseCircleOutline,
IoStopCircleOutline,
IoPlaySkipBackCircleOutline,
IoPlaySkipForwardCircleOutline,
} from "react-icons/io5";
const PlayStory = (props) => {
const { base64adio } = props;
const [playing, isPlaying] = useState(false);
const [audioFile, setAudioFile] = useState(null);
const [duration, setDuration] = useState("00:00");
const [currentTime, setCurrentTime] = useState("00:00");
const handleStop = () => {
audioFile.pause();
audioFile.currentTime = 0;
isPlaying(false);
};
const handleSkipBack = () => {
const newTime = audioFile.currentTime - 10;
audioFile.currentTime = newTime < 0 ? 0 : newTime;
};
const handleSkipForward = () => {
const newTime = audioFile.currentTime + 10;
audioFile.currentTime =
newTime > audioFile.duration ? audioFile.duration : newTime;
};
const handlePlayPause = () => {
if (audioFile) {
if (playing) {
audioFile.pause();
} else {
audioFile.play();
}
isPlaying((pre) => !pre);
}
};
useEffect(() => {
if (base64adio) {
const audio = new Audio(`data:audio/x-wav;base64, ${base64adio}`);
setAudioFile(audio);
audio.onloadeddata = () => {
const audioDuration = audio.duration;
if (typeof audioDuration === "number" && !isNaN(audioDuration)) {
setDuration(formatTime(audioDuration));
}
};
}
}, [base64adio]);
useEffect(() => {
if (audioFile) {
// // Update current time while audio is playing
audioFile.ontimeupdate = () => {
const duration = audioFile.duration;
const currentTime = audioFile.currentTime;
if (duration === currentTime) {
isPlaying(false);
audioFile.currentTime = 0;
audioFile.pause();
setCurrentTime(formatTime(audioFile.currentTime));
} else {
setCurrentTime(formatTime(currentTime));
}
};
}
return () => {
if (audioFile) {
audioFile.onloadeddata = null; // Cleanup on unmount
audioFile.ontimeupdate = null; // Cleanup on unmount
}
};
}, [audioFile]);
return (
<div className="flex items-center justify-between mx-1">
{currentTime}
<IoPlaySkipBackCircleOutline
className="cursor-pointer text-gray-800 size-6"
size={25}
onClick={handleSkipBack}
/>
{playing ? (
<IoPauseCircleOutline
onClick={handlePlayPause}
className="cursor-pointer text-gray-800 size-6"
size={25}
/>
) : (
<IoPlayCircleOutline
onClick={handlePlayPause}
className="cursor-pointer text-gray-800 size-6"
size={25}
/>
)}
<IoStopCircleOutline
className="cursor-pointer text-gray-800 size-6"
size={25}
onClick={() => handleStop()}
/>
<IoPlaySkipForwardCircleOutline
className="cursor-pointer text-gray-800 size-6"
size={25}
onClick={handleSkipForward}
/>
{duration}
</div>
);
};
export default PlayStory;
const formatTime = (seconds) => {
const minutes = Math.floor(seconds / 60);
const remainingSeconds = Math.floor(seconds % 60);
return `${minutes.toString().padStart(2, "0")}:${remainingSeconds
.toString()
.padStart(2, "0")}`;
};
Conclusion
This audio player component provides a great way to integrate audio playback controls in React applications. With customizable controls and a clear visual layout, it’s easy to add, modify, or expand its functionalities.
Posted on November 7, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.