Music Player Using ReactJS

Building A Music Player Using ReactJS ( Source Code )

Introduction

In this tutorial, we’ll walk you through the process of creating how to build a completely functional Music Player from scratch. The responsive Music Player will purely will be user friendly and User interactive. ReactJS is a JavaScript Framework just like Angular and VueJS Frameworks but instead, it is a more popular one.

Preview Of Music Player Application

Music Player Using ReactJS

Prerequisite

  • Basic Knowledge of HTML
  • Basic Knowledge of CSS
  • Basic Knowledge of JS including ReactJS Concepts.

Setup the Environment

Before we can start building our Music Player, we need to set up our environment. We’ll use create-react-app .

To create a new application, open your terminal or command prompt and type:

npx create-react-app Music-Player

This command will create a new folder named `Music-Player`  with all the necessary files and dependencies for our React application.

The React template is finally set up and we are ready to build the clone!

50+ Interesting React JS Project ideas With Source Code from Beginner and Intermediate-level

Let’s Build a Music Player Using React.js!!!

Let’s be clear First, start creating the files and folder and then we’ll start implementing the code.

Your Folder Structure should look like the below:-

Folder Structure Music Player using React.js

Create the Component Folder and add the following Folders and files:-

Folder Structure Music Player using React.js

AboutHeader.js

import React from "react";
import AboutHeaderTitle from "../../Elements/About/AboutHeaderTitle";
import AboutHeaderCloseIcon from "../../Elements/About/AboutHeaderCloseIcon";
function MenuHeader({ uiState, setUiState }) {
    return (
        <nav className="nav__header">
            <AboutHeaderTitle />
            <AboutHeaderCloseIcon uiState={uiState} setUiState={setUiState} />
        </nav>
    );
}

export default MenuHeader;

MenuHeader.js

import React from "react";
import MenuTitle from "../../Elements/Common/MenuTitle";
import MenuIcon from "../../Elements/Common/MenuIcon";
function MenuHeader({ uiState, setUiState }) {
    return (
        <nav className="nav__header">
            <MenuTitle />
            <MenuIcon uiState={uiState} setUiState={setUiState} />
        </nav>
    );
}

export default MenuHeader;

LibraryHeder.js

import React from "react";
import LibraryHeaderTitle from "../../Elements/Library/LibraryHeaderTitle";
import LibraryHeaderCloseIcon from "../../Elements/Library/LibraryHeaderCloseIcon";
function MenuHeader({ uiState, setUiState }) {
    return (
        <nav className="nav__header">
            <LibraryHeaderTitle />
            <LibraryHeaderCloseIcon uiState={uiState} setUiState={setUiState} />
        </nav>
    );
}

export default MenuHeader;

LibraryListItem.js

import React from "react";
import songData from "../../Data/SongData";
import LibrarySongArtist from "../../Elements/Library/LibrarySongArtist";
import LibrarySongCover from "../../Elements/Library/LibrarySongCover";
import LibrarySongTitle from "../../Elements/Library/LibrarySongTitle";

function LibraryListItem({ song, setSongState, songState, audioRef }) {
    // console.log(song.id === songState.currentSong[0].id);
    // currentSong: [songData[(currentIndex + 1) % songData.length]],

    const changeCurrentSongHandler = () => {
        setTimeout(() => {
            setSongState({
                ...songState,
                currentSong: [songData[songData.findIndex((s) => s === song)]],
            });
            console.log(songState.isPlaying);
            if (songState.isPlaying) {
                const playPromise = audioRef.current.play();
                console.log(playPromise);
                if (playPromise !== undefined) {
                    console.log("undefined");
                    playPromise.then((audio) => {
                        audioRef.current.play();
                    });
                }
            }
        }, 150);
    };
    return (
        <div
            onClick={changeCurrentSongHandler}
            className={`library__list-item ${
                song.id === songState.currentSong[0].id ? "active-song" : ""
            }`}
        >
            <LibrarySongCover song={song} />
            <div className="library__song-column">
                <LibrarySongTitle song={song} />
                <LibrarySongArtist song={song} />
            </div>
        </div>
    );
}

export default LibraryListItem;

PlayerControl.js

import React, { useRef } from "react";
import {
    RiPlayListLine,
    RiSunLine,
    RiMoonLine,
    RiSkipBackLine,
    RiSkipForwardLine,
} from "react-icons/ri";
import songData from "../../Data/SongData";
import PlayerPlayButton from "../../Elements/Main/PlayerPlayButton";

function PlayerControl({
    uiState,
    setUiState,
    songState,
    setSongState,
    audioRef,
}) {
    let currentIndex = songData.findIndex(
        (song) => song === songState.currentSong[0]
    );

    const previousSongHandler = () => {
        setTimeout(() => {
            if ((currentIndex - 1) % songData.length === -1) {
                setSongState({
                    ...songState,
                    currentSong: [songData[songData.length - 1]],
                });
            } else {
                setSongState({
                    ...songState,
                    currentSong: [
                        songData[(currentIndex - 1) % songData.length],
                    ],
                });
            }
            if (songState.isPlaying) {
                const playPromise = audioRef.current.play();
                if (playPromise !== undefined) {
                    playPromise.then((audio) => {
                        audioRef.current.play();
                    });
                }
            }
        }, 300);
    };

    const nextSongHandler = () => {
        setTimeout(() => {
            setSongState({
                ...songState,
                currentSong: [songData[(currentIndex + 1) % songData.length]],
            });
            if (songState.isPlaying) {
                audioRef.current.play();
            }
        }, 150);
    };

    const darkModeToggleHandler = () => {
        setUiState({ ...uiState, darkMode: !uiState.darkMode });
    };

    const libraryToggleHandler = (e) => {
        if (window.visualViewport.width < 900) {
            setUiState({ ...uiState, libraryShown: true });
            console.log("changed");
        }
    };

    const songEndHandler = async () => {
        await setSongState({
            ...songState,
            currentSong: [songData[(currentIndex + 1) % songData.length]],
        });
        if (songState.currentSong[0].isPlaying) {
            const playPromise = audioRef.current.play();
            if (playPromise !== undefined) {
                playPromise.then((audio) => audioRef.current.play());
            }
        }
    };

    const DarkModeButton = () => {
        if (!uiState.darkMode) {
            return (
                <RiMoonLine
                    className="player__control-icon"
                    onClick={darkModeToggleHandler}
                />
            );
        } else {
            return (
                <RiSunLine
                    className="player__control-icon"
                    onClick={darkModeToggleHandler}
                />
            );
        }
    };

    return (
        <div className="player__control">
            <RiPlayListLine
                uiState={uiState}
                setUiState={setUiState}
                className="player__control-icon disabled-on-desktop"
                onClick={libraryToggleHandler}
            />
            <RiSkipBackLine
                className="player__control-icon"
                onClick={previousSongHandler}
            />
            <PlayerPlayButton
                uiState={uiState}
                setUiState={setUiState}
                setSongState={setSongState}
                songState={songState}
                audioRef={audioRef}
            />
            <RiSkipForwardLine
                className="player__control-icon"
                onClick={nextSongHandler}
            />
            <DarkModeButton />
        </div>
    );
}

export default PlayerControl;

SeekControl.js

import React from "react";
import PlayerDuration from "../../Elements/Main/PlayerDuration";
import PlayerSeekBar from "../../Elements/Main/PlayerSeekBar";

function SeekControl({ songState, setSongState, audioRef, seekWidth }) {
    const getTime = (time) => {
        return (
            Math.floor(time / 60) +
            ":" +
            ("0" + Math.floor(time % 60)).slice(-2)
            // get the time and divide it by 60 - 155/60 = 2.59
            // Floor the value - 2
            // append ":" to the number from previous step
            // divide the time by 60 and get the remainder - 155 % 60 (remainder = 35)
            // Now floor this remainder to obtain a smaller value - getting rid of decimals
            // prepend "0" to the number obtained from previous step
            // slice the number to get only the last 2 digits
        );
    };
    return (
        <div className="player__seek-controls">
            <PlayerDuration value={`${getTime(songState.elapsed)}`} />
            <PlayerSeekBar
                songState={songState}
                setSongState={setSongState}
                audioRef={audioRef}
                seekWidth={seekWidth}
            />
            <PlayerDuration
                value={`${
                    getTime(songState.duration) === "NaN:aN"
                        ? "0:00"
                        : getTime(songState.duration)
                }`}
            />
        </div>
    );
}

export default SeekControl;

SongInfo.js

import React from "react";
import SongInfoTitle from "../../Elements/Main/SongInfoTitle";
import SongInfoArtist from "../../Elements/Main/SongInfoArtist";
function SongInfo({ songState }) {
    return (
        <div className="song-info">
            <SongInfoTitle songState={songState} />
            <SongInfoArtist songState={songState} />
        </div>
    );
}

export default SongInfo;

Your App.js File will be the main entry point for your Application.

App.js

import React, { useRef, useState } from "react";
import "./Styles/app.scss";
import MenuHeader from "./Components/Common/MenuHeader";
import Artwork from "./Elements/Main/Artwork";
import SongInfo from "./Components/Main/SongInfo";
import Player from "./Components/PlayerInterface/Player";
import Library from "./Layouts/Library";
import About from "./Layouts/About";
import songData from "./Data/SongData";

function App() {
    // Detect if the user has dark mode turned on
    let userDarkModeApplied = window.matchMedia(
        "(prefers-color-scheme: dark)"
    ).matches;

    // UI Components State
    const [uiState, setUiState] = useState({
        aboutShown: false,
        libraryShown: false,
        libraryPinned: false,
        darkMode: userDarkModeApplied ? true : false,
        coverSpinning: false,
        songPlaying: false,
        seekWidth: 0,
    });
    // Song States
    const [songState, setSongState] = useState({
        currentSong: [songData[0]],
        isPlaying: false,
        elapsed: 0,
        duration: 0,
    });

    // Reference for the audio
    const audioRef = useRef(null);

    // Setting the background as the cover artwork
    document.body.style.backgroundImage = `url('${songState.currentSong[0].coverUrl}')`;

    const songEndHandler = async () => {
        let currentIndex = songData.findIndex(
            (song) => song === songState.currentSong[0]
        );
        await setSongState({
            ...songState,
            currentSong: [songData[(currentIndex + 1) % songData.length]],
        });
        audioRef.current.play();
    };

    const songInfoHandler = (e) => {
        const elapsed = e.target.currentTime;
        const duration = e.target.duration;
        setSongState({
            ...songState,
            duration: duration,
            elapsed: elapsed,
        });
    };

    return (
        <div
            className={`app__wrapper ${
                uiState.darkMode ? "dark-mode" : "light-mode"
            }`}
            style={{
                backdropFilter: `${
                    uiState.libraryShown || uiState.aboutShown
                        ? "none"
                        : "blur(1.5rem)"
                }`,
                WebkitBackdropFilter: `${
                    uiState.libraryShown || uiState.aboutShown
                        ? "none"
                        : "blur(1.5rem)"
                }`,
            }}
        >
            {/* The menu header only displays the menu options */}
            {/* It only needs access to isNavMenuShown, setNavMenuShown, */}
            <MenuHeader uiState={uiState} setUiState={setUiState} />
            <Artwork uiState={uiState} songState={songState} />
            <SongInfo songState={songState} />
            <Player
                uiState={uiState}
                setUiState={setUiState}
                audioRef={audioRef}
                songState={songState}
                setSongState={setSongState}
            />
            <Library
                uiState={uiState}
                setUiState={setUiState}
                songState={songState}
                setSongState={setSongState}
                songData={songData}
                audioRef={audioRef}
            />
            <About uiState={uiState} setUiState={setUiState} />
            <audio
                ref={audioRef}
                src={songState.currentSong[0].audio}
                onTimeUpdate={songInfoHandler}
                onLoadedMetadata={songInfoHandler}
                onEnded={songEndHandler}
            ></audio>
        </div>
    );
}

export default App;

Index.js

import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
import * as serviceWorkerRegistration from "./serviceWorkerRegistration";
// import reportWebVitals from "./reportWebVitals";

ReactDOM.render(
    <React.StrictMode>
        <App />
    </React.StrictMode>,
    document.getElementById("root")
);

// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://cra.link/PWA
serviceWorkerRegistration.register();

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
// reportWebVitals();

GitHub Repo Free Source Code

For the Rest of the code implementation, you can follow the GitHub repo I’ve mentioned below or you can clone that repo.

Music Clone Using ReactJS

Final Output Of Our Music Player

Building a To-Do List App Using ReactJS

Conclusion

Congratulations, You have completed your Major Reactjs Project and further you can enhance this as per your preferences. You can deploy it on the Github, Vercel, or Netfliy platforms and make it live for your friends. Add this to your resume after adding or enhancing more functionality like a timer, Adding Music, Making Playlist, saving Favourite Music and many more. You can add further functionalities to your websites.

ADVERTISEMENT

I hope you liked this Tutorial and must have learned Something new. If you have any questions regarding this feel free to drop your comments below and contact our team on Instagram @Codewith_random.

ADVERTISEMENT

Happy Coding!!!

ADVERTISEMENT



Leave a Reply