Building a Music Entertainment Application with ReactJS, NextJS, Algolia, and Firebase
Tags: react, javascript, webdev, tutorial
Published: 2021-10-22 08:21:50

_by author [Tuduo Victory](https://blog.openreplay.com/authors/tuduo-victory)_

A music streaming application, as the name implies, is simply an application that lets you stream music on your device. Streaming is a process by which we listen to music or watch a video in real-time rather than downloading it on our device. Some apps that offer this service include Spotify, Netflix, and Audiomack, among others. In this tutorial, we will be building a simple music application where we can upload music of our choice and stream them directly from the app. We will also learn how to use Google Firebase Cloud storage, access the Firestore database and use Algolia service to provide data from our database to our application.

## **Setting up our Application**

For the purpose of this tutorial, we will be making use of different technologies in building our application. We will be building basically a [NextJS](https://nextjs.org/) front-end application with [Firebase Cloud](https://firebase.google.com/docs/storage) [storage](https://www.googleadservices.com/pagead/aclk?sa=L&ai=DChcSEwiBxeTA1tPzAhXDB5EKHcYLA-4YABAAGgJjZQ&ae=2&ei=P0JtYe2WBs-p1sQP8dKegAQ&ohost=www.google.com&cid=CAESQeD2t0RQ9fmuJ54F6CgCN-CobOfJUtR2Ca-mZPk14lNkwykrV6EqxxQsNnTGav0ds3gGnq0CbK7V5mmCmd_6rNEw&sig=AOD64_16WhxjtOVYCD3JRju5uIBj1103ww&q&sqi=2&adurl&ved=2ahUKEwitst3A1tPzAhXPlJUCHXGpB0AQ0Qx6BAgDEAE), [Firestore database](https://firebase.google.com/docs/firestore), and [Algolia](https://www.algolia.com/) on the back-end. NextJS is an open source development framework built on NodeJS that allows React-based applications to be rendered server-side. Firebase Cloud storage is a cloud-hosted storage that lets us store files of any kind on the cloud while Firestore refers to the NoSQL real-time database on Firebase that lets you build collaborative apps by allowing secured access to your database directly from the server-side. Algolia is a hosted search engine that can provide real-time results on search from the very first keystroke entered.

### **Installing dependencies**
Firstly, we will set up our work environment by installing our front-end framework which is NextJS. Run the following code in our Command Line Interface in the chosen directory where we want to create this application.

for npm users:

```
npx install create-next-app musicapp --use-npm
```

for yarn users:

```
npx install create-next-app musicapp
```

In the above bash scripts, we created a folder called `musicapp` which has the NextJS package installed. This will serve as our work folder.


## **Configuring our page**

Our newly created application is set up to use the NextJS framework and has a template in it upon installation. In our index.js file, we will clear the code within our return block and replace it with the following:


```javascript
import Head from 'next/head'
export default function Home() {
 return (
   <div >
     <h1>components here</h1>
   </div>
)
}
```

The import head is used to add custom title or meta tag data to our web application. The head is used as illustrated below:


```javascript
import Head from 'next/head'
export default function Home() {
 return (
   <div>
     <head>
       <title>Music Application</title>
       <meta name="keywords" content="music, streaming, entertainment"></meta>
     </head>
     <h1>components here</h1>
   </div>
);
}
```


Here we created a head and gave our app the title "Music Application". We also defined some keywords for our metadata search words.

NextJS allows for easy routing without having to configure a 3rd-party router within our application. In ReactJS we had to install and use react-routers to handle routing between our pages in our application. NextJS already has routers set up for us. We just need to create a new page in the pages folder and we can route it to our browser with the path.
create a new file called library.js in your pages folder with the following:


```javascript
import React from "react";
import Head from 'next/head'
const library = () => {
 return (
   <div>
      <head>
       <title>Library</title>
       <meta name="keywords" content="music, streaming, entertainment"></meta>
     </head>
     <h1>Music LIbrary here:</h1>
   </div>
);
};
export default library;
```


This will be our page for uploading music to our cloud storage and database. We will cover building this page later on in this tutorial.
now if we run our program using the following commands in our CLI we can see the output of our application in our browser:


```
npm run dev
```

Navigate to the URL our server is hosted on, we see the homepage as our `index.js` component.


![landing page](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kczbczdhbe8djow1bwz2.png)


Adding "/library" to our URL will redirect you to our `library.js` component:


![music library](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lvmkh7705sraw0rfpy6v.png)


 Back in our `index.js` file. we will be using the react `useState` hook:


```javascript
import {UseState} from 'react';
```

UseState is a built-in hook for React. It allows us to add states to our functional components. Using this we can create a state without switching to class components.

To show how the elements will be aligned and the music player works, we will be using dummy data supplied from our work folder. For this, create a folder called images and another called songs within our public folder. items within the public folder are directly accessible to every page of our application in NextJS.
In my image folder I have two pictures named "img1" and "img2". I also have two song files in my song folder. We will use this dummy data with the `use``S``tate` react hook as follows:



```javascript
export default function Home() {
 const [songs, setsongs] = useState([
  {
     title: "song 1",
     artist: "artist 1",
     img_src: "./images/img1.jpg",
     src: "./songs/Måneskin - Beggin ( Testo)_2.mp3",
  },
  {
     title: "song 2",
     artist: "artist 2",
     img_src: "./images/img2.jpg",
     src: "./songs/Young Dumb & Broke Khalid .mp3",
  },
]);
 return (
   <div>
     <head>
       <title>Music Application</title>
       <meta name="keywords" content="music, streaming, entertainment"></meta>
     </head>
     <h1>components here</h1>
   </div>
);
}
```


The paths to your images will be in the `img_src` and that to your songs will be in `src`.

We will then create a folder within our `src` folder called components. Here, we will have the different components that make up our application. we will have two major components: a `play.js` component and a `search.js` component.

In our working directory, there is a file called `_app.js` within the pages folder. This file lets us render pages on our app server-side. We will create a component that will wrap around all our pages. To do this, create a file called Layout.js within our component folder. we will also create a style sheet for this component called layout.module.css. Note that .module.css is the naming convention for style sheet files in NextJS. In the following code, I have created a Layout component that adds a header and a footer to our page. Since the `_app.js` renders children as props. You can bring that prop and use it within our layout component as shown below:


```javascript
import React from "react";
import style from "../styles/layout.module.css";
const Layout = ({children}) => {
 return (
   <div className={style.container}>
     <div className={style.top}>
       <h3>Music Streaming</h3>
         </div>
        {children}
     <div className={style.footer}>
       <h3>Browse and listen to music of your choice</h3>
     </div>
   </div>
);
};
export default Layout;
```

In our `layout.module.css` file we have the following styles:


```css
.container {
 font-weight: bold;
 color: #333;
}
.top,
.footer {
   height: 50px;
   width: 100%;
   color:#fff;
   background: rgb(73, 13, 236);
   display: flex;
   align-items: center;
   padding-left: 15px;
}

```

Then, in our `_app.js` file, we will import our Layout component and then wrap our component `props` within the layout as shown below:


```javascript
import Layout from '../components/Layout'
import '../styles/globals.css'
function MyApp({ Component, pageProps }) {
 return (
   <Layout>
     <Component {...pageProps} />
   </Layout>
);
}
export default MyApp
```


If we return to our browser, on our main page we will have the output below:


![home after wrapping layout component](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ny795fds6hx2l5tyxbht.png)


Note that if you navigate to the "/library" path in the URL you will still have the Layout component wrapping it also as it is part of the component props.

For our music player, we will create three additional components called `Player.js`, `PlayerDetail.js`, and `PlayerControls.js` within our component folder. We will also create a style sheet called player.module.css and import it within our Player.js. Open `Player.js` and populate it with the following code:


```javascript
import React from 'react'
import style from "../styles/player.module.css"
function Player() {
   return (
       <div className={style.cplayer}>
           <audio></audio>
           <h4>Playing now</h4>
          {/*music search functionality */}
          {/*player Details here */}
          {/*plaer controls here */}
           <p><strong>Next up: </strong>next song here</p>
       </div>
  )
}
export default Player
```


Back in our index.js we are going to set additional variables using the `usestate` hook that will handle the particular song being played.


```javascript
const [currentSongIndex, setCurrentSongIndex] = useState(0);
const [nextSongIndex, setNextSongIndex] = useState(currentSongIndex + 1);
```


We will then add imports for our Player component to our `index.js` file.  Add the following code to do this:


```javascript
import Player from "../components/Player"
```

After this, we add the `Player` component within our `Home` component and return two `props` with it which will be the song and the next song.


```javascript
import Head from "next/head";
import Player from "../components/Player"
import { useState } from "react";
export default function Home() {
 ...
 const [currentSongIndex, setCurrentSongIndex] = useState(0);
 const [nextSongIndex, setNextSongIndex] = useState(currentSongIndex + 1);
 return (
   <div>
     <head>
       <title>Music Application</title>
       <meta name="keywords" content="music, streaming, entertainment"></meta>
     </head>
     <h1>components here</h1>
     <Player
       song={songs[currentSongIndex]}
       nextSong={songs[nextSongIndex]}
     />
   </div>
);
}
```


These props will be used to indicate the current song playing and the next song to be played. We can then reference these props within our Player component in `Player.js`.


```javascript
function Player(props) {
   return (
       <div className={style.cplayer}>
           <audio></audio>
           <h4>Playing now</h4>
          {/*music search functionality */}
          {/*player Details here */}
          {/*plaer controls here */}
           <p><strong>Next up: </strong>{props.nextSong.title} by {props.nextSong.artist}</p>
       </div>
  )
}
```


If you return to your browser you will have an output similar to the image below:


![components added to home page](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4wmb1mdywbs5380v9ymt.png)


To add player details to our app, add the following code to our `PlayerDetails` component earlier created:


```javascript
import React from 'react'
import style from "../styles/player.module.css"
function PlayerDetails(props) {
   return (
       <div className={style.playerdetails}>
           <div className={style.detailsimg}>
               <img src={props.song.img_src} alt=""></img>
           </div>
           <h3 className={style.detailstitle}>{props.song.title}</h3>
           <h3 className={style.detailsartist}>{props.song.artist}</h3>
       </div>
  )
}
export default PlayerDetails
```


We will then import this within our `Player.js` component


```javascript
import PlayerDetails from './PlayerDetails'
```

We can now add the component within our Player and pass the `props` of the song to it as follows:


```javascript
function Player(props) {
   return (
       <div className={style.cplayer}>
           <audio></audio>
           <h4>Playing now</h4>
          {/*music search functionality */}
           <PlayerDetails song={props.song}/>
          {/*plaer controls here */}
           <p><strong>Next up: </strong>{props.nextSong.title} by {props.nextSong.artist}</p>
       </div>
  )
}
```


In our browser, you will have the image shown along with the title and artist of the song.

We will be making use of the react-audio package to play our audio. to do this press `ctrl+c` to terminate your server in your terminal, then install using:
for npm users:

```
npm i react-h5-audio-player
```

for yarn users:

```
yarn add react-h5-audio-player
```

This `pkg` has music player controls such as seek, volume, and others pre-built for use. We just need to import it for use within our application.


```javascript
import React from "react";
import style from "../styles/player.module.css";
import AudioPlayer from "react-h5-audio-player";
import "react-h5-audio-player/lib/styles.css";
function PlayerControls(props) {
 return (
   <div className={style.playercontrols}>
     <AudioPlayer
       autoPlay
       src={props.song.src}
       onPlay={(e) => console.log("onPlay")}
       // other props here
       showSkipControls
       autoPlayAfterSrcChange

     />
   </div>
);
}
export default PlayerControls;
```


After this, we import this player control component into our Player.js file and add it to our `Home`


```javascript
function Player(props) {
   return (
       <div className={style.cplayer}>
           <audio></audio>
           <h4>Playing now</h4>
          {/*music search functionality */}
           <PlayerDetails song={props.song}/>
           <PlayerControls song={props.song}/>
           <p><strong>Next up: </strong>{props.nextSong.title} by {props.nextSong.artist}</p>
       </div>
  )
}
```


For our search functionality, we will create a new file called `Search.js` in our component folder. We will set some custom styles for this component to show where it will be placed in our Application. We will cover building this component later in this tutorial. In our `Search.js` file we have the following codes:


```javascript
import React from 'react'
import style from "../styles/search.module.css"
function Search() {
   return (
       <div className={style.searchcont}>
          {/*search*/}
       </div>
  )
}
export default Search
```


In our `search.module.css` file we have:


```css
.searchcont{
   height:100%;
   width: 60%;
   background: #ddd;
}

```

Then, we import this component into our `index.js`  file and arrange it side by side with the player component using a new style sheet called `arrangement.module.css`.


```javascript
import Head from "next/head";
import Player from "../components/Player"
import Search from "../components/Search"
import { useState } from "react";
import style from "../styles/arrangement.module.css"
export default function Home() {
...
 return (
   <div className={style.maincont}>
     <head>
       <title>Music Application</title>
       <meta name="keywords" content="music, streaming, entertainment"></meta>
     </head>
     <Search/>
     <Player
       song={songs[currentSongIndex]}
       nextSong={songs[nextSongIndex]}
     />
   </div>
);
}
```


In the `arrangement.module.css` we have the following styles:


```css
.maincont{
   display: flex;
}
```


Now, we can proceed with styling our music player in `player.module.css` stylesheet:


```css
.cplayer{
   margin: 0;
   box-sizing: border-box;
   font-family: monospace;
   background: #313131;
   color:#fff;
   width: 100%;
   display: flex;
   align-items: center;
   justify-content: center;
   min-height: 100vh;
   flex-direction: column;
   border-top-left-radius: 13px;
   border-bottom-left-radius: 13px;
   padding: 50px;
   padding-top: 3px;
   /* box-shadow: inset -6px -6px 12px rgba(0,0,0,.8); */
}
.cplayer h4{
   font-size: 14px;
   text-transform: uppercase;
   font-weight: 400;
   text-align: center;
}
.cplayer > p{
   color: #aaa;
   font-size: 14px;
   text-align: center;
   font-weight: 400;
}
.playerdetails .detailsimg{
   position: relative;
   width: fit-content;
   margin: 0 auto;
}
.detailsimg img{
   display: block;
   margin: 0px auto;
   width: 100%;
   max-width: 250px;
   border-radius: 50%;
   box-shadow: 6px 6px 12px rgba(0,0,0,.8), -6px -6px 12px rgba(255,255,255,0.4);
}
.detailstitle{
   color: #eee;
   font-size: 20px;
   text-shadow: 2px 2px 4px rgba(0,0,0,.8), -2px -2px 4px rgba(255,255,255,0.4);
   text-align: center;
   margin-bottom: 10px;
}
.detailsartist{
   color: #aaa;
   font-size: 20px;
   text-shadow: 2px 2px 4px rgba(0,0,0,.8), -2px -2px 4px rgba(255,255,255,0.4);
   text-align: center;
   margin-bottom: 20px;
}
```


After this, we will build our search component with Algolia.


## **Algolia**

### **Create a user account**
To create a user account, navigate in our browser to [](https://www.algolia.com/)[Algolia](https://www.algolia.com/) and click on Start Free. You can create an account using the available options.

### **Create an index called Music**
Upon creation of the account, you will be prompted to create an index. An index refers to the place where the data which would be used by the search engine is stored. This is equivalent to what "tables" are for databases. We would create an index called Music. 

### **Customization**
You can customize searchable attributes and rankings based on our configuration. The former specifies the search keywords to be used in filtering through our search and the latter defines the words that are used to order our items. For now, select sample data set media.
In the configuration, you can set searchable attributes to the attribute of your choice. Under ranking and sorting, click on “add custom ranking”, “post date” to make the search show the most recent results first. You can test how it works in the run demo option.

Next, we will create our database on Firebase which will automatically update our Algolia content when changes are made.


## **Firebase**

Google Firebase is a Google-backed application development software that enables developers to develop apps for different platforms. Firebase provides us with a real-time database: Firestore. Firestore is a no-SQL database which lets us build high performance applications with the same features offered by traditional databases. With it we can easily store, synchronize and fetch data for our application.

### **Create Project**
Navigate to [Firebase](https://firebase.google.com/) in your browser, click on “Get Started”, sign in and create a new project. Key in the name of your project, check the other options, and create a new project called Music application. 

You can now install the firebase package for use within your application. Press `ctrl+c` to end server in your terminal and type:


```
npm install --save firebase
npm install -g firebase-tools
firebase init functions
```

Select Yes as the default for everything, select Use existing apps, and select the app we created. Select install dependencies at the end. Once the process is finished, in the CLI run:


```
cd functions
```

Next, you will need to set up your app and `API` keys. you can find these on your dashboard in Algolia.


```
firebase functions:config:set algolia.app=Your_app_id algolia.key=admin_api_key
```


We will need to use firebase functions to link the data in our Firestore database to our Algolia index. **Note that you need to be on the blaze plan on firebase to use functions**. To do this, click on the extensions in the dashboard and select Algolia. It will then require you to select the “index”, then the name of the Firestore collection. In this case, we will create a collection called Music. Leave the attribute field to be indexed blank so that all fields in the collection will be indexed. We can add the Algolia ID and API keys gotten from the Algolia dashboard. Then click on install extension to finalize.

### **Adding dummy data to Firebase database**
To test how our Firestore database works with Algolia, we will use faker.js npm package to populate our firebase database and reflect it in Algolia.


    npm install faker

Create a new file called `seed.js` in the `functions` folder. We will use this to populate our firebase with faker details.

We will also need to add the Google Cloud Firestore API to our app and  create a key on [](https://console.cloud.google.com/)[google Cloud](https://console.cloud.google.com/) for our application.


![](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/akhe7vafoe081idfgox9.png)


Download and bring the key to your project directory. Then in your Firebase project settings, under Service Accounts, copy the code and add the `path` to your key in the space required.


```javascript
const admin = require("firebase-admin");
var serviceAccount = require("path to your key");
paste admin.intializeApp code here
const faker = require("faker");
const db = admin.firestore();
const fakeIt = () => {
 return db.collection("Music").add({
   username: faker.internet.userName(),
   email: faker.internet.email(),
   avatar: faker.internet.avatar(),
});
};
Array(5).fill(0).forEach(fakeIt);

```

To run this, open the command shell window and key in:


```
node seed.js
```

Automatically it would create a new database collection for us called "Music". If we navigate to our index on Algolia we will see that the new data created by faker on our database is shown there.


![Data generated by faker](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/era96yu7hd7ahfu7hfsi.png)


In `search.js` we will use `React instant search` package to create our search component. To install this, in your command window key in:

```
npm install algoliasearch react-instantsearch-dom react-instantsearch
```

Then in our `search.js` component, set up with the following code:


```javascript
import React from "react";
import style from "../styles/search.module.css";
import algoliasearch from "algoliasearch/lite";
import { InstantSearch, SearchBox, Hits } from "react-instantsearch-dom";
const searchClient = algoliasearch(
 "algolia id",
 "algolia key"
);
function Search() {
 return (
   <div className={style.searchcont}>
     <InstantSearch searchClient={searchClient} indexName="Music">
             <SearchBox translations={{placeholder: 'Search for music'}}/>
             <Hits/>
     </InstantSearch>
   </div>
);
}
export default Search;
```


This will return the hits from Algolia to our search component. You can use the search bar to search through these hits based on the attributes we earlier defined. We will delete this data from firebase and proceed to create our upload page to upload music to firebase, we will then style our search component and play the chosen music on click.

## Open Source Session Replay
Debugging a web application in production may be challenging and time-consuming. [OpenReplay](https://github.com/openreplay/openreplay) is an Open-source alternative to FullStory, LogRocket and Hotjar. It allows you to monitor and replay everything your users do and shows how your app behaves for every issue.
It’s like having your browser’s inspector open while looking over your user’s shoulder.
OpenReplay is the only open-source alternative currently available.

![OpenReplay](https://raw.githubusercontent.com/openreplay/openreplay/main/static/replayer.png)

Happy debugging, for modern frontend teams - [Start monitoring your web app for free](https://github.com/openreplay/openreplay).


## **Uploading media Content to Firebase**

To create a music upload page in `Library.js`, firstly we will create and import a CSS file for `Library.js`, then create a new file called `Upload.js` in the components folder and add an import for it in the Library file. In `Upload.js` we will create the components to upload music to our firebase. We will store the records in the Firestore database and store the files in Firebase storage. Within `Library.js`:


```javascript
import Upload from "../components/Upload"
import style from "../styles/library.module.css"
```


Here we have added imports for the Upload component and the stylesheet.

To prevent an error message that occurs when we initialize firebase multiple times we would create a file separately for firebase config and import it when we need to initialize firebase. Create a file called `firebase.js` in your root folder and populate it with the following code:


```javascript
import firebase from "firebase";
import "firebase/firestore";
const firebaseConfig = {
//you will find this in your project settings on firebase
};
!firebase.apps.length
 ? firebase.initializeApp(firebaseConfig).firestore()
: firebase.app().firestore();
var db = firebase.firestore();
export default db
```


 
To use firebase storage we need to first create a storage in our firebase account. You can do this by clicking on storage on the dashboard, then new. In our `Upload.js`:


```javascript
import React from "react";
import style from "../styles/library.module.css";
import db from "../firebase"
import firebase from "firebase";
import "firebase/storage"
function Upload() {
   const changed = (e) =>{
       var file = e.target.files[0];
       var storageRef = firebase.storage().ref("Image");
       const fileRef = storageRef.child(file.name)
       fileRef.put(file).then(() => {
           console.log("uploaded", file.name)
      })


  }
   function submit(e) {
       e.preventDefault();
  }
   return (
       <div>
           <form onSubmit={submit}>
               <input type="file" className={style.myfile} onChange={changed} />
           <input type="text" name="name" placeholder="Music name"/>
               <button>Submit</button>
           </form>
           <progress min="0" max="100" value="65"/>
       </div>
  )
}
export default Upload
```


This creates two input fields: one for text and the other for a file. The `onChange` event handler in the input file type is used to run a function that uploads whatever file that is added in the input to our firebase storage.

 Note that to allow uploads to our Firebase cloud storage, we would need to adjust the rules from our dashboard as shown in the image below.
 

![Storage Rules Changed](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/94upny229o0ml1tiwc5o.png)


Adjusting this rule lets us use our cloud storage without having to be authenticated. This is suitable for development but in the case of deploying it's advisable to use the normal rule. 

 If we add an image to the input we will see the image in our Firebase Storage within the image folder

![uploaded image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zu5qcgyepxdrabsp3u04.png)


 After uploading our files to firebase storage, we need to get the `URL` to these files to reference them in our Firestore database. To do that we will run an `async` request that will wait until the file has been uploaded to firebase then we will assign the download `URL` to a variable. Also, we have disabled the button on the form to submit to our database until the file upload is resolved.



```javascript
function Upload() {
   const [fileUrl, setFileUrl] = React.useState(null)
   const [musicUrl, setMusicUrl] = React.useState(null)
   const [disable, setDisable] = React.useState(true);

  React.useEffect(() => {
    if (musicUrl !== null && fileUrl !== null) {
      setDisable(false);
      alert("click on submit")
      console.log(disable)
    }
  },[musicUrl, fileUrl])
   const filechanged = async (e) =>{
       var file = e.target.files[0];
       var storageRef = firebase.storage().ref("Image");
       const fileRef = storageRef.child(file.name)
       await fileRef.put(file)
       setFileUrl(await fileRef.getDownloadURL())    
  }
   const musicchanged = async (e) =>{
       var music = e.target.files[0];
       var storagemRef = firebase.storage().ref("Music");
       const musicRef = storagemRef.child(music.name)
       await musicRef.put(music)
       setMusicUrl(await musicRef.getDownloadURL())   
  }
 const submit =  (e) => {
    e.preventDefault();  
      const musicname = e.target.musicname.value;
      if (!musicname) {
        return
      }
      db.collection("Music").doc(musicname).set({
        name: musicname,
        music: musicUrl,
        image: fileUrl
      })
      alert("Music added") 
}
   return (
     <div className={style.uploadpage}>
       <form onSubmit={submit} className={style.uploadform}>
         <label>images</label>
         <input
           type="file"
           className={style.myfile}
           name="img"
           onChange={filechanged}
           required
         />
         <label>Music files</label>
         <input type="file" name="music" onChange={musicchanged} required />
         <input
           type="text"
           name="musicname"
           placeholder="Music name"
           required
         />
         <button className={style.btn} disabled={disable}>Submit</button>
       </form>
     </div>
  );
}
export default Upload
```


   
We also need to edit the rules for the Firestore database.


![](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6ftirnvf4a1qg0o0yko2.png)


We can now add input for the music file, we get an alert when the files have been uploaded telling us to click submit, then we can upload the data to our database when we click the “submit” button. We now see that the record in our database now has a field with the music `URL`.


![Firestore after image and music upload](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fzvb4zo098vlpy7rp5yk.png)


Add some styles to the `library.module.css` file to make this page look better.


```css
.uploadpage{
   height: 80vh;
   display: flex;
   justify-content: center;
}
.h{
   text-align: center;
}
.uploadform{
   display: flex;
   flex-direction: column;
   justify-content: center;
   align-items: center;
}
.uploadform input{
   margin: 10px 0 15px 0;
   outline: none;
   padding: 10px;
}
.uploadform input[file]{
   padding: 10px;
}
.uploadform label{
   text-transform: uppercase;
}
.uploadform button{
   border: none;
   padding: 10px 25px;
   background: rgb(73, 13, 236);
   border-radius: 15px;
   color: #fff;
}
```


We will now proceed to style our `search` component and add functionality to it.
In Algolia, we can set criteria in configuration for our searchable attributes and the ranking:


![searchable attribute](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xtitaqoo74kts3zpbrq2.png)


Then for ranking:

![ranking](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hipitpmnp2o8lvaiap6p.png)

 To proceed with our `search` component:


```javascript
import React from "react";
import style from "../styles/search.module.css";
import algoliasearch from "algoliasearch/lite";
import { InstantSearch, SearchBox, Hits } from "react-instantsearch-dom";
const searchClient = algoliasearch(
 "algolia id",
 "algolia key"
);
const Hit = ({hit}) => {
 return (<div className={style.hit}>
   <div className={style.artist} onClick={handleClick}>
     <h4>{hit.name}</h4>
   </div>
 </div>)
}
const Content = () => {
 return(<div className={style.content}>
   <Hits hitComponent={Hit}/>
 </div>)
}
function Search() {
 return (
   <div className={style.searchcont}>
     <InstantSearch searchClient={searchClient} indexName="Music">
             <SearchBox translations={{placeholder: 'Search for music'}}/>
       <main>
         <Content/>
       </main>
     </InstantSearch>
   </div>
);
}
export default Search;
```


Here we rendered a component called Content into the main tag of search. In Content we have the Hits component which renders each `hitComponent` following the structure we defined in `Hit`. We created an `h4` element which values the `hit.name` which we got from our database through Algolia.


![](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/i5c93r4s26ydqpwclmxr.png)


We will add the click functionality for these individual div items in the `onClick` event handler.


```javascript
const Hit = ({ hit }) => {
 const handleClick = () => {
   var playimg = hit.image;
   var playsong = hit.music;
   var title = hit.name;
};
 return (<div className={style.hit}>
   <div className={style.artist} onClick={handleClick}>
     <h4>{hit.name}</h4>
   </div>
 </div>)
}
```


We will style our `search.module.css` with the following styles to give it a better appearance.


```css
.searchcont {
  height: 100vh;
  overflow: auto;
  width: 60%;
  padding: 0 10px;
}
.hit {
  background: #333;
  color: #fff;
  padding: 15px 0 15px 10px;
  list-style-type: none;
  border-radius: 5px;
  box-shadow: 6px 6px 8px rgba(0,0,0,.8), -6px -6px 8px rgba(211, 211, 211, 0.151);
  margin: 13px 0;
}

.hit:hover{
  cursor: pointer;
}

.searchcont li {
  list-style-type: none!important;
}
.searchcont ul {
  padding: 0;
}
.search input{
  border: none;
  padding: 10px 15px;
  margin: 10px 5px;
  outline: none;
}
.search button{
  border: none;
  padding: 10px 15px;
  background: #eee;
}
```
## **Using the React player to play selected media**

We will then delete all dummy data we used for our music source, image and title. Presently in our index.js, we have the following code:


```javascript
import Head from "next/head";
import Player from "../components/Player"
import Search from "../components/Search"
import { useState } from "react";
import style from "../styles/arrangement.module.css"
export default function Home() {
 return (
   <>
     <head>
       <title>Music Application</title>
       <meta name="keywords" content="music, streaming, entertainment"></meta>
     </head>
   <div className={style.maincont}>
     <Search/>
     <Player />
     </div>
     </>
);
}
```


You will need to pass the variable for our `playimg`, `playsong`, and `playtitle` to the  Player component and use within it. To do this we will first make the variables we just listed `states` using `useState` hook since their values will change as our program runs. 


```javascript
...
function Search() {
  const [Image, setImage] = React.useState(null);
  const [title, setTitle] = React.useState(null);
  const [playing, setPlaying] = React.useState(null);
  const searchClient = algoliasearch(
    "algolia id",
    "algolia key"
  );

  const Hit = ({ hit }) => {
    const handleClick = () => {
       setImage(hit.image);
       setPlaying(hit.music);
       setTitle(hit.name);
    };
    return (<div className={style.hit}>
      <div className={style.artist} onClick={handleClick}>
        <h4>{hit.name}</h4>
      </div>
    </div>)
  }
...
```


Then in our `index.js` file we will now create state values to store the data we will pull from the `search` component.



```javascript
export default function Home(props) {
  const [a, b] = useState(null)
  const [song, newsong] = useState(null)
  const [image, newimage] = useState(null)
  const pull_data = (data) => {
    b(data);
  }
  const pull_song = (data) => {
    newsong(data);
  }
  const pull_img = (data) => {
    newimage(data);
  }

  return (
    <>
      <head>
        <title>Music Application</title>
        <meta name="keywords" content="music, streaming, entertainment"></meta>
      </head>
      <div className={style.maincont}>
        <Search  func={pull_data} song={pull_song} image={pull_img}/>
        <Player title={a} play={song} image={image}/>
      </div>
    </>
  );
}
```
    

Here we created and passed props to the `Search` component which returned values that were assigned to state variables. These state variables were later passed as `props` down to our `Player` component. In our `Search` we assigned values to them as shown below


```javascript
function Search(props) {
...
  props.func(title);
  props.song(playing)
  props.image(Image)

  const Hit = ({ hit }) => {
    const handleClick = () => {
       setImage(hit.image);
       setPlaying(hit.music);
       setTitle(hit.name);
    };
```
       

Here we assigned the `props` with values of tile, playing, and Image earlier defined, respectively.

We used the passed props in our `Player` component and passed it down to the `Playerdetails` and `PlayerControls` components.


```javascript
function Player(props) {
    return (
        <div className={style.cplayer}>
            <audio></audio>
            <h4>Playing now</h4>
            <PlayerDetails title={props.title} image={props.image}/>
            <PlayerControls song={props.play}/>
        </div>
    )
}
```


We could then use the passed `props` within our `playerDetails`:


```javascript
function PlayerDetails(props) {
  return (
    <div className={style.playerdetails}>
      <div className={style.detailsimg}>
        <img src={props.image} alt=""></img>
      </div>
      <h3 className={style.detailstitle}>{props.title}</h3>
    </div>
  );
}
```


Here we have set the title of our `h3` to the value passed down in `props.title`.

We also added `src` for our music through the props passed to the `PlayerControls` component.


```javascript
function PlayerControls(props) {
  return (
    <div className={style.playercontrols}>
      <AudioPlayer
        src={props.song}
        showSkipControls
        autoPlayAfterSrcChange   
      />
    </div>
  );
}
```


Now if we run:

```
npm run dev
```

Then navigate to our browser we can play any song we have uploaded.


![](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qv372201o63wf4qs2mwt.png)


To make the player image have a constant size we would add some code to the style in our `player.module.css`. 


```css
.detailsimg img{
    display: block;
    margin: 0px auto;
     height: 250px;
    width: 250px;
    border-radius: 50%;
    box-shadow: 6px 6px 12px rgba(0,0,0,.8), -6px -6px 12px rgba(255,255,255,0.4);
}
```
    

Here we simply assign height and width attributes to our image to ensure it will always be rounded.


![Round image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/k8i762ho2k1ozoipylb7.png)


We can also add a button to navigate to our upload page. To use `links` in NextJS we need to import it first. We will create a separate file for this in our components folder and call it `Nav.js` we will then import this file into our `Layout` component so it can always wrap around all our pages. We will create a new style sheet for this component called `nav.module.css` and also import it within our file.


```javascript
import React from 'react'
import Link from "next/link"
import style from "../styles/nav.module.css"
function Nav() {
    return (
        <div className={style.nav}>
            <ul>
                <li>
                    <Link href="/">Home</Link>
                </li>
                <li>
                    <Link href="/library">Upload music</Link>
                </li>
            </ul>
        </div>
    )
}
export default Nav
```


Then we have the following styles in `nav.module.css`:

```css
.nav{
    color: #fff;
    display: flex;
    background: #333;
    justify-content: flex-start;
    align-items: center;
    height: 50px;
    padding: 10px;
    margin-left: 10px;
}
.nav ul{
    display: flex;
    justify-content: center;
    align-items: center;
    list-style: none;
}
.nav ul li a{
    margin: 5px 15px;
}
```


We will add this new component to our `Layout`


```javascript
const Layout = ({children}) => {
  return (
    ...
        <Nav/>
          </div>
          {children}
      <div className={style.footer}>
        <h3>Browse and listen to music of your choice</h3>
      </div>
    </div>
  );
};
```


Now we have the navigation bar displaying on both our pages.


![added navigation bar](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ocqmj47y5zivf6me0fed.png)



## Conclusion

In this tutorial we covered building a music entertainment application with different technologies. We learned what these technologies were and their uses in the world today.


## Resources

Link to Github resource: [Github](https://github.com/Victory-ET/Music-Player)