In this article, we’re going to look at another fun way to build with Strapi. We’ll be building a music player app! We’ll be using Strapi as our backend, where our music files would be stored and fetched into our app. Think of it like a streaming app, yes! A streaming app like Spotify.
Strapi is an open-source content management system. It allows you to create customizable APIs in any frontend application. Strapi is so easy to use because it allows you to build flexible APIs with one-of-a-kind features that you'll love.
You can create custom content types and relationships between them to keep things organized. It also includes a media library for storing images and audio files. This is one of the many features of Strapi. Let’s get started.
To follow through with this article, you must have the following:
Before we proceed, it is very important to know the scope of our project. What we want to archieve is build a simple music player in which all its music and details including the name of the artist, name of song and image acting as the cover music. Our music App should also be able to show us the next song on the list and the artist.
To get started, we’ll first need to install Strapi. Create a folder called strapi-musicplayer
, cd into it in our terminal, and run either of the following commands below:
npx create-strapi-app@latest my-project --quickstart
#or
yarn create strapi-app my-project --quickstart
This will install all the necessary packages for this project. After installation, our Strapi app will be launched automatically in our browser. We should have something like this:
To start our project any other time, we use the command:
npm run devlop
We will be directed to the admin homepage after registering to configure our backend content and APIs. Let’s move to create our collection type.
In order to create our collection type, on your admin homepage, go to Content-Type Builder and create a new collection type.
Give it a display name that is singular, not plural. It is automatically pluralized by Strapi. We will refer to our collection as music_player
.
For our music_player
, we’ll need a title in form of text type, the name of the artist also of the type text, the image source and the music source in form of media type.
Let’s go ahead and create them.
Click on Save to save our collection and. Let’s move on to populate our collection.
In the top-left corner of the admin page, select Content Manager. This will navigate you to the page where we will add contents to our database.
You can add as many songs with each of its corresponding details which includes the image, the title and artist. Once we are done, we can either save and test it first, or we skip that and go ahead to publish it.
To make the music_player
available to consume it in our React frontend, navigate to Roles
under Users and Permissions
Plugins. Then, click on the public
and scroll down to permissions.
Under the Portfolio dropdown, select the find
and findOne
options by clicking on Public. This will make the portfolio content available to the public.
Whenever we try to retrieve it by using the Strapi API, it sends us the data. Now that we’re done, let’s move on to the frontend of our project.
For our frontend, we’ll be using React. Let's get started by installing React and all the necessary packages.
npx create-react-app music-player
cd
into the just installed folder: cd music-player
npm install axios
npm i --save @fortawesome/fontawesome-svg-core
npm install --save @fortawesome/free-solid-svg-icons
npm install --save @fortawesome/react-fontawesome
Before we start up our app, let’s get rid of the files that we won’t be using in our src
folder these are reportwebvitals
, App.css
, App.test.js
, logo.svg
, and setupTest.js
. We also want to make some cleanup to our remaining files. for our index.js
, we clean it up to look like this:
The next is our index.css
. We’ll be deleting it all, leaving us with a blank file. Lastly, for our app, we’ll clean it up to look like this:
Now that we’ve done that, let’s start our app.
npm start
We have gotten everything cleaned and sorted out. So, let’s proceed with our project.
creating components is the next thing we’ll be doing. We’ll be creating three components, namely:
Index.js
: This is where we’ll be adding our music API and also other functionalities.
Player.js
: This will be responsible for handling everything concerning our music player. Think of it as the captain of a squad, in which our Detials.js
and Control.js
are under because it’ll be using their info ( this will be our props) to work.
Details.js
: This will contain details like the artist name and title of the song.
Controls.js
This will be responsible for the controls like the play, pause, next, and previous.Inside our src
folder, let’s create a new folder called components
. Inside the folder, we’ll create our file called index.js
, Player.js
, Details.js
, Controls.js
Beginning with index.js
, paste this in it:
1 import axios from "axios";
2 import { useEffect, useState } from "react";
3 import Player from "./Player";
4 const Index = () => {
5 const [songs, setsongs] = useState([]);
6 const [currentSongIndex, setCurrentSongIndex] = useState(0);
7 const [nextSongIndex, setNextSongIndex] = useState(0);
8 // fetching our api
9 useEffect(() => {
10 const fetchData = async () => {
11 try {
12 const { data: response } = await axios.get(
13 "http://localhost:1337/api/music-players?populate=*"
14 );
15 let _musics = response.data;
16 _musics.map((music) => {
17 let pload = {
18 title: music.attributes.title,
19 artist: music.attributes.artist,
20 img_src:
21 "http://localhost:1337" +
22 music.attributes.img_src.data[0].attributes.url,
23 src:
24 "http://localhost:1337" +
25 music.attributes.music_src.data[0].attributes.url,
26 };
27 setsongs((oldSongs) => [...oldSongs, pload]);
28 });
29 } catch (error) {
30 console.error(error);
31 }
32 };
33 fetchData();
34 }, []);
35 // .. calling
36 useEffect(() => {
37 setNextSongIndex(() => {
38 if (currentSongIndex + 1 > songs.length - 1) {
39 return 0;
40 } else {
41 return currentSongIndex + 1;
42 }
43 });
44 }, [currentSongIndex]);
45 // ..
46 return (
47 <div className="App">
48 {songs.length > 0 && (
49 <>
50 <Player
51 currentSongIndex={currentSongIndex}
52 setCurrentSongIndex={setCurrentSongIndex}
53 nextSongIndex={nextSongIndex}
54 songs={songs}
55 />
56 </>
57 )}
58 </div>
59 );
60 };
61 export default Index;
In Player.js, paste:
1 import React, { useState, useRef, useEffect } from "react";
2 import Controls from "./Controls";
3 import Details from "./Details";
4 function Player(props) {
5 const audioEl = useRef(null);
6 const [isPlaying, setIsPlaying] = useState(false);
7 useEffect(() => {
8 if (isPlaying) {
9 audioEl.current.play();
10 } else {
11 audioEl.current.pause();
12 }
13 });
14 const SkipSong = (forwards = true) => {
15 if (forwards) {
16 props.setCurrentSongIndex(() => {
17 let temp = props.currentSongIndex;
18 temp++;
19 if (temp > props.songs.length - 1) {
20 temp = 0;
21 }
22 return temp;
23 });
24 } else {
25 props.setCurrentSongIndex(() => {
26 let temp = props.currentSongIndex;
27 temp--;
28 if (temp < 0) {
29 temp = props.songs.length - 1;
30 }
31 return temp;
32 });
33 }
34 };
35 return (
36 <div className="my-player">
37 <audio
38 src={props.songs[props.currentSongIndex].src}
39 ref={audioEl}
40 ></audio>
41 <h4>Playing now</h4>
42 <Details song={props.songs[props.currentSongIndex]} />
43 <Controls
44 isPlaying={isPlaying}
45 setIsPlaying={setIsPlaying}
46 SkipSong={SkipSong}
47 />
48 <p>
49 Next up:{" "}
50 <span>
51 {props.songs[props.nextSongIndex].title} by{" "}
52 {props.songs[props.nextSongIndex].artist}
53 </span>
54 </p>
55 </div>
56 );
57 }
58 export default Player;
In Controls.js
, paste:
1 import React from "react";
2 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
3 import {
4 faPlay,
5 faPause,
6 faForward,
7 faBackward,
8 } from "@fortawesome/free-solid-svg-icons";
9 function Controls(props) {
10 return (
11 <div className="my-player--controls">
12 <button className="skip-btn" onClick={() => props.SkipSong(false)}>
13 <FontAwesomeIcon icon={faBackward} />
14 </button>
15 <button
16 className="play-btn"
17 onClick={() => props.setIsPlaying(!props.isPlaying)}
18 >
19 <FontAwesomeIcon icon={props.isPlaying ? faPause : faPlay} />
20 </button>
21 <button className="skip-btn" onClick={() => props.SkipSong()}>
22 <FontAwesomeIcon icon={faForward} />
23 </button>
24 </div>
25 );
26 }
27 export default Controls;
In Details.js
, paste:
1 import React from "react";
2 function Details(props) {
3 return (
4 <div className="my-player--details">
5 <div className="details-img">
6 <img src={props.song.img_src} alt="" />
7 </div>
8 <h3 className="details-title">{props.song.title}</h3>
9 <h4 className="details-artist">{props.song.artist}</h4>
10 </div>
11 );
12 }
13 export default Details;
For our index
, we imported our player component, we then used our axios
to fetch our data and also put them in an array which we would be working with. We are performing a logic that will display our current song and also our next song to be played. We’ll be grabbing the info from our Player.js
.
For our player.js
, we are importing our Details
and Controls
components. It houses both components. Here, we are configuring the functionalities for our song display, details and controls like he play, the pause,the skip song and the previous song.
Controls.js
contains the control interface of our app like the play , pause and all. Our Player.js
uses it’s props to configure the functionalities since it would be bulky if we included it all in the same file.
Details
is used to display the details of the music played. Our Player.js
also uses the its props.
We just have to do two more things. The first is to pass in our index component into our App.js
, since it holds the other components.
1 import React from "react";
2 function Details(props) {
3 return (
4 <div className="my-player--details">
5 <div className="details-img">
6 <img src={props.song.img_src} alt="" />
7 </div>
8 <h3 className="details-title">{props.song.title}</h3>
9 <h4 className="details-artist">{props.song.artist}</h4>
10 </div>
11 );
12 }
13 export default Details;
Lastly, let’s style it. In index.css
, paste this:
1 * {
2 margin: 0;
3 padding: 0;
4 box-sizing: border-box;
5 font-family: "Fira Sans", sans-serif;
6 }
7 body {
8 background-color: #DDD;
9 }
10 .App {
11 display: flex;
12 align-items: center;
13 justify-content: center;
14 min-height: 100vh;
15 }
16 .my-player {
17 display: block;
18 background-color: #313131;
19 display: block;
20 margin: 0px auto;
21 padding: 50px;
22 border-radius: 16px;
23 box-shadow: inset -6px -6px 12px rgba(0, 0, 0, 0.8), inset 6px 6px 12px rgba(255, 255, 255, 0.4);
24 }
25 .my-player > h4 {
26 color: #FFF;
27 font-size: 14px;
28 text-transform: uppercase;
29 font-weight: 500;
30 text-align: center;
31 }
32 .my-player > p {
33 color: #AAA;
34 font-size: 14px;
35 text-align: center;
36 font-weight: 600;
37 }
38 .my-player > p span {
39 font-weight: 400;
40 }
41 .my-player--details .details-img {
42 position: relative;
43 width: fit-content;
44 margin: 0 auto;
45 }
46 .my-player--details .details-img img {
47 display: block;
48 margin: 50px auto;
49 width: 100%;
50 max-width: 250px;
51 border-radius: 50%;
52 box-shadow: 6px 6px 12px rgba(0, 0, 0, 0.8), -6px -6px 12px rgba(255, 255, 255, 0.4);
53 }
54 .my-player--details .details-img:after {
55 content: '';
56 display: block;
57 position: absolute;
58 top: -25px;
59 left: -25px;
60 right: -25px;
61 bottom: -25px;
62 border-radius: 50%;
63 border: 3px dashed rgb(0,0,255);
64 }
65 .my-player--details .details-title {
66 color: #EEE;
67 font-size: 28px;
68 text-shadow: 2px 2px 4px rgba(0,0,0,0.8), -2px -2px 4px rgba(255,255,255,0.4);
69 text-align: center;
70 margin-bottom: 10px;
71 }
72 .my-player--details .details-artist {
73 color: #AAA;
74 font-size: 20px;
75 text-shadow: 2px 2px 4px rgba(0,0,0,0.8), -2px -2px 4px rgba(255,255,255,0.4);
76 text-align: center;
77 margin-bottom: 20px;
78 }
79 .my-player--controls {
80 display: flex;
81 align-items: center;
82 justify-content: center;
83 margin-bottom: 30px;
84 }
85 .my-player--controls .play-btn {
86 display: flex;
87 margin: 0 30px;
88 padding: 20px;
89 border-radius: 50%;
90 box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.8), -4px -4px 10px rgba(255, 255, 255, 0.4), inset -4px -4px 10px rgba(0, 0, 0, 0.4), inset 4px 4px 10px rgba(255, 255, 255, 0.4);
91 border: none;
92 outline: none;
93 background-color: #0000FF;
94 color: #FFF;
95 font-size: 24px;
96 cursor: pointer;
97 }
98 .my-player--controls .skip-btn {
99 background: none;
100 border: none;
101 outline: none;
102 cursor: pointer;
103 color: #888;
104 font-size: 18px;
105 }
Save all and check the result in our browser.
We just built ourselves a music player! Click here to access the full code on my GitHub Repo.
We have seen another innovative way to use Strapi by building a music player. Special thanks to Asaolu Elijah, who contributed to this article in a very crucial way.
I'm a web developer and writer. I love to share my experiences and things I've learned through writing.