This is the second guide of the series that explains how to create and launch a clone music app using React 18, Tailwind, Redux, and Rapid API Hub.
The first part of the guide focused on the React music player's file and folder structure, and it provided instructions on how to run the app on a local server and view its basic layout.
In this part of the guide, we will explore how to fetch music from the Rapid API Hub, and we'll discuss the overall route structure and development of the Discover.jsx
page, which is a critical component of the app's user interface.
Before continuing, you can go ahead and sign up for Rapid API Hub to access thousands of APIs that we can use for our project.
Moving forward, we will return to the code by minimizing our terminal, accessing the source files, and beginning with the App.jsx
file.
First, we will discuss the file's structure, including a general route configuration. The first route listed is the Discover
, and by holding the Ctrl
or Cmd
key and clicking on it, we will be directed to the Discover.jsx
page, which functions as an independent page within the application.
js
import { useSelector } from 'react-redux';import { Route, Routes } from 'react-router-dom';import { Searchbar, Sidebar, MusicPlayer, TopPlay } from './components';import { ArtistDetails, TopArtists, AroundYou, Discover, Search, SongDetails, TopCharts } from './pages';const App = () => {const { activeSong } = useSelector((state) => state.player);return (<div className="relative flex"><Sidebar /><div className="flex-1 flex flex-col bg-gradient-to-br from-black to-[#121286]"><Searchbar /><div className="px-6 h-[calc(100vh-72px)] overflow-y-scroll hide-scrollbar flex xl:flex-row flex-col-reverse"><div className="flex-1 h-fit pb-40"><Routes><Route path="/" element={<Discover />} /><Route path="/top-artists" element={<TopArtists />} /><Route path="/top-charts" element={<TopCharts />} /><Route path="/around-you" element={<AroundYou />} /><Route path="/artists/:id" element={<ArtistDetails />} /><Route path="/songs/:songid" element={<SongDetails />} /><Route path="/search/:searchTerm" element={<Search />} /></Routes></div><div className="xl:sticky relative top-0 h-fit"><TopPlay /></div></div></div><MusicPlayer song={activeSong} /></div>);}export default App;
Our next step is constructing the Discover page, consisting of the interface shown in the image below. This interface will be the final structure of our application.
Initially, we will create a div
element for the discover icon, followed by a genre selector at the top. We will then retrieve all of the songs from an API and display them, along with hover effects and visually appealing artwork. Let's start this in the following steps.
We will begin by opening our Discover.jsx
file on one side of the screen and the local server at https://localhost:3000/ on the other side, allowing us to work simultaneously on both the code and the app's visual display.
To begin creating our Discover
page, we will import several other components necessary for its implementation. These components include the Error
component, the Loader
component, and the SongCard
component. Each of these components is simply a div that displays the name of the individual element. Importing these components is necessary, as we will require them to create our Discover
page.
Additionally, we can retrieve the genres
data from assets/constants
by importing it into our Discover.jsx
page. By clicking on the genres
file while holding the Ctrl
or Cmd
key, we can observe that it is an array comprising of objects, with each object containing two key-value pairs. The title
key holds a human-readable name for the genre, while the value
key consists of an all-uppercase string with underscores.
Now that we have imported our necessary components and the genres
data, we can create the return
statement for our Discover.jsx
page. We can also add a console.log(genres)
statement to check if the genre data is being correctly retrieved.
js
import { Error, Loader, SongCard } from '../components';import { genres } from '../assets/constants';const Discover = () => {console.log(genres);return (<div>Discover</div>);}export default Discover;
In this step, we will begin by creating the structure. We will start by adding a div
element with a className
of flex
and flex-col
for column layout. It's important to note that we are using Tailwind CSS for styling and have a Tailwind extension installed to help us visualize each property. You can find and install the Tailwind CSS Intelligence extension by searching for it in the extensions marketplace within Visual Studio Code.
Additionally, Tailwind has an official documentation website where you can find detailed explanations and examples for each class. You can access it at this link. Here is the updated code for our Discover
page:
js
import { Error, Loader, SongCard } from '../components';import { genres } from '../assets/constants';const Discover = () => {console.log(genres);return (<div className="flex flex-col">Discover</div>);}export default Discover;
In this step, we will create the Discover page by adding another div
inside the parent div
. This new div
will have a className
value of w-full
for full width and flex justified-between items-center
to align the items in a justified manner with space between them.
On small devices, this div
will have a flex-row
display property and a flex-column
for larger devices. Additionally, we will add a margin-top
property with a value of mt-four
and mb-10
to set the margin at the top and bottom, respectively.
We will add a select
component for the genre selection inside this' div'. However, we must also add an h2
element above the select component. We will create the h2
element with the text content of Discover
and assign it some class names such as font-bold
and text-3xl
to make it more prominent.
Additionally, we will assign it text-white
and text-left
class names to change its color to white and align the text to the left.
js
import { Error, Loader, SongCard } from '../components';import { genres } from '../assets/constants';const Discover = () => {console.log(genres);return (<div className="flex flex-col"><div className="w-full flex justify-between items-center flex-row md:flex-col mt-4 mb-10"><h2 className="font-bold text-3xl text-white text-left">Discover</h2><select>{genres.map((genre) => (<option key={genre.value} value={genre.value}>{genre.title}</option>))}</select></div></div>);};export default Discover;
In this step, we will hard code the title as Pop
for now, but we will change it later. Our select
element will have some basic properties that most input elements have, such as the onChange()
property, which we can leave empty for now, and the value()
property, which we will set to an empty string for the time being.
Next, we will add a className
attribute to the select
element, which will apply some styles, such as a black background color, gray text color, and rounded corners. The p-3
and text-sm
classes will set the padding and font size of the element, respectively.
The outline-none
class will remove the default outline around the element when it is focused. Additionally, the sm:mt-@
and mt-5
classes will add margin to the top of the element on different screen sizes.
js
import { Error, Loader, SongCard } from '../components';import { genres } from '../assets/constants';const Discover = () => {console.log(genres);const genreTitle = "Pop";return (<div className="flex flex-col"><div className="w-full flex justify-between items-center flex-row md:flex-col mt-4 mb-10"><h2 className="font-bold text-3xl text-white text-left">Discover</h2><selectonChange={() => {}}value=""className="bg-black text-gray-400 rounded p-3 text-sm outline-none sm:mt-5 mt-5">{genres.map((genre) => (<option key={genre.value} value={genre.value}>{genre.title}</option>))}</select></div></div>);};export default Discover;
To implement the above, we can add the map()
method to the select
element and iterate over the genres
array. We can create an option
element for each genre with the value
attribute set to the genre's value
property and the text content set to the genre's title
property. This will display all available genres as selectable options for the user.
js
import { Error, Loader, SongCard } from '../components';import { genres } from '../assets/constants';const Discover = () => {console.log(genres);const genreTitle = "Pop";return (<div className="flex flex-col"><div className="w-full flex justify-between items-center flex-row md:flex-col mt-4 mb-10"><h2 className="font-bold text-3xl text-white text-left">Discover</h2><selectonChange={() => {}}value=""className="p-3 text-sm bg-black text-gray-400 rounded outline-none sm:mt-5 mt-3">{genres.map((genre) => (<option key={genre.value} value={genre.value}>{genre.title}</option>))}</select></div></div>);};export default Discover;
This is how our application looks like after this step:
To create the wrapper for our songs, we can add another div
element below the first one inside the return
statement of the Discover
component. This new div
will serve as a container for the song cards that we will render.
We will assign a CSS class name flex flex-wrap
to the new div
. On small devices, we will set its alignment to sm:justify-start
, but typically it should be centered using justify-center
with a gap of gap-8
.
We will then iterate over the songs fetched from the API and display them inside this div
. However, for now, we will create a sample array of numbers ranging from 1 to 10, and consider each number as a song.
We need to include the SongCard
component and provide it with some information. Firstly, we need to set the key
prop of the SongCard
component to be equal to song.key
.
Additionally, we need to pass the entire song
object to the SongCard
component as a prop, with song
being equal to song
. Finally, we also need to provide the index of each song, which is available as the second parameter i
of the map
function.
jsx
import React from 'react';import { useDispatch, useSelector } from 'react-redux';import { Error, Loader, SongCard } from '../components';import { genres } from '../assets/constants';const Discover = () => {const genreTitle = `Pop`;return (<div className="flex flex-col"><div className="w-full flex justify-between items-center sm:flex-row flex-col mt-4 mb-10"><h2 className="font-bold text-3xl text-white text-left">Discover {genreTitle}</h2><selectonChange={() => {}}value=""className="bg-black text-gray-300 p-3 text-sm rounded-lg outline-none sm:mt-0 mt-5">{genres.map((genre) => (<option key={genre.value} value={genre.value}>{genre.title}</option>))}</select></div><div className="flex flex-wrap sm:justify-start justify-center gap-8">{[1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map((song, i) => (<SongCard key={song.key} song={song} i={i} />))}</div></div>);};export default Discover;
That's all the information we need to pass at the moment. As a result, we will now see the SongCard
repeated ten times.
The reason why the Discover component was started with is because it leads us to the most crucial functionalities of the application, which are fetching songs from the Shazam Core API and then styling and displaying them.
In this guide, we've gone through the process of building a music Lyriks website using React, Tailwind CSS, and the Rapid API Hub.
We started by setting up the project and creating a layout for the homepage. Then, we added a dropdown menu to select music genres and displayed a list of demo songs using the SongCard
component.
Along the way, we covered various topics such as React hooks, handling events, conditional rendering, and CSS styling with Tailwind. Stay tuned for the next part of the guide.