How to Build a Spotify Individual Playlist Page in React
Build out and style a page for each playlist and let users sort tracks by audio features
Get the project source code below, and follow along with the lesson material.
Download Project Source CodeTo set up the project on your local machine, please follow the directions provided in the README.md
file. If you run into any issues with running the project source code, then feel free to reach out to the author in the course's Discord channel.
This lesson preview is part of the Build a Spotify Connected App course and can be unlocked immediately with a \newline Pro subscription or a single-time purchase. Already have access to this course? Log in here.
Get unlimited access to Build a Spotify Connected App, plus 70+ \newline books, guides and courses with the \newline Pro subscription.

[00:00 - 00:18] Alright, so the last page we're going to build out is the individual playlist page. With this page, we're going to display all of the tracks in a playlist as well as add a way for users to sort songs by different audio features, like Dancibility, Tempo, and Energy.
[00:19 - 00:46] So just like all of the other pages, I'm going to add an import to the top of our app.js and then down in the route, I'm going to replace this H1 with the playlist component that we're going to create in just a second. And note that unlike all of the other routes, this route has an ID URL parameter, and we're going to be using this ID to fetch the playlist we want from Spotify.
[00:47 - 01:07] So up here in the pages directory, I'm going to add a file called playlist.js, and then I'll stub that out. And then I'll export it from our index.js file.
[01:08 - 01:28] And then after I give that all a save, I can click on one of my playlists, and we'll see that our playlist page component is rendering. So what we want to do now is grab the ID off of the URL, and we want to use that ID to hit this get playlist endpoint from the Spotify API.
[01:29 - 01:41] And the way we're going to do that is by adding a function to our Spotify.js file. So in our Spotify.js file, I'm going to add another function here called get playlist by ID.
[01:42 - 01:55] And this function is pretty simple. It takes a playlist ID as an argument, which is the ID for the playlist, and then it calls a get request to the slash playlist slash playlist ID endpoint.
[01:56 - 02:12] Then in our playlist.js file, we're going to import this get playlist by ID function. And like all of our other pages, we are going to use a use state hook and a use effect hook to fetch the data and then render some information in our template.
[02:13 - 02:34] So over here, I'm going to add this use effect that fetches data from the get playlist by ID function passes the ID from this use params hook from react router DOM. And then we'll set the data we get back from that function as our playlist variable.
[02:35 - 02:54] And then we use the playlist variable to render this header, which we're using the styled header style component here. And basically, just like we have with our profile page, I'm using that header component to render the playlist name and the total number of songs it has.
[02:55 - 03:18] So if we take a look at the data that's coming back from our get playlist by ID function, we'll console log that. So if we take a look at what's being logged here, we'll see that there is an object with a bunch of information on it, but there's a tracks property here with an items array with all of the track objects for all of the tracks on this playlist.
[03:19 - 03:39] But you'll notice that similar to the playlist data we were dealing with for the playlist page, there is a paging object being returned here with an href next URL, limit offset and previous. So if you take a look at the total, it looks like I have 278 tracks on this playlist.
[03:40 - 03:57] But since the limit is on 100, we're only getting 100 items back with this API column. So we're going to have to implement the same kind of logic from the playlist page to render all the tracks and update our template to render the track list component we have.
[03:58 - 04:08] So I'll update my playlist.js file. So we just added a second use effect hook here, just like our playlist page.
[04:09 - 04:21] And we keep track of the tracks data as well as an array of tracks to render our track list component. And basically, we check if there is a next URL on the tracks data object.
[04:22 - 04:35] And if there is, we send a get request to that URL to get the next set of tracks. And then once we have compiled all of the tracks in an array, the tracks array, we pass that to the track list component.
[04:36 - 04:43] So it looks like there's an error in our app now. And this is because we're passing data to the track list component in a way that it doesn't expect.
[04:44 - 04:53] So for the track list component, it's expecting a tracks prop. And on the tracks prop, we're mapping through each track.
[04:54 - 05:19] And we're getting like the album, the name, the artists on that track object. But if we take a look at what we console log before in this items array, each track is an object, but each object has another track property on it with all of the data our track list component is expecting like album, like images, like name.
[05:20 - 05:43] So to make sure we pass the right structure of data to our track list component , we'll just create an array of memoized tracks that's structured in a way that works with the component. So to do that, we're going to use the use memo react hook, and we'll create an array down here called tracks for track list.
[05:44 - 05:54] And this will take tracks as a dependency. And since tracks is initialized to null at first, we'll just return if it's falsie.
[05:55 - 06:16] But if it's not, we'll return tracks.map and we'll destructure the track property off of the track object and then simply return that track. All right, we can console log this and then pass that to our track lists.
[06:17 - 06:24] All right, so give it a refresh. And it looks like all of our tracks are being rendered.
[06:25 - 06:45] And if you take a look at the logs, you'll see that there is 100, 200, then 278 because of the paging objects. All right, so now that we have our track list rendering properly, let's take a look at what we have so far.
[06:46 - 07:08] So in this playlist JS file, first I am using react routers use params hook to grab the ID peram off of the URL. And then we are passing that ID to our get playlist by ID function from our Spotify JS file, which hits the slash playlist slash playlist ID endpoint.
[07:09 - 07:25] And notice that ID is a dependency for this first use effect hook because we need it to call this function. And then the other thing we're doing here is keeping track of the playlist data as well as the tracks property on the playlist object.
[07:26 - 07:59] And we're doing this so that we can handle retrieving all of the tracks for the playlist, not just the first 100, which is the limit on the JSON response. And then after we've compiled an array of all of the tracks on the playlist, then we have a use memo function here that's creating this tracks for track list array, which is mapping through each track object and then returning the track property on it, which has the album, the images, the name that we need for our track list component.
[08:00 - 08:13] And finally, we are passing it to the track list component here. The next thing we want to do is add a way to sort these tracks by audio feature .
[08:14 - 08:34] And to do that, we're going to be using this get audio features for several tracks API endpoint from the Spotify API. And basically, we can send this API endpoint, a IDs query parameter of a comma separated list of Spotify IDs for all of the tracks we want to get audio features for.
[08:35 - 08:54] And it'll just respond with an array of objects containing audio features for the tracks we requested. So first, I'm going to head on over to my Spotify JS file, and I'm going to add a function that sends a get request to this endpoint, the slash audio features endpoint.
[08:55 - 09:10] And I'm going to have this function take an argument that's the track IDs, and then pass that as the values for this IDs query parameter. Then I'm going to import this function to the playlist file.
[09:11 - 09:34] And then I'm going to add another use date hook here to keep track of the audio features array. And down in our second use effect hook here, I'm going to add another function here that's going to be called fetch audio features.
[09:35 - 09:57] I'm going to wrap the catch errors, higher order function around it. And this function is going to map over our tracks data items every time this logic runs, and it's going to compile a comma separated list of IDs that we're going to pass to our get audio features for tracks function, which then will return data that is an array of audio features.
[09:58 - 10:15] And just like we did for tracks, we're going to merge that data into our existing audio features state variable. Let's console log our audio features to see what that looks like.
[10:16 - 10:32] So once the use effect hooks finish running, we will have 278 items in this array because there's 278 tracks. And if we take a look at what these objects look like, they're objects of audio features.
[10:33 - 10:53] So basically for each track, it returns the ID, but it also returns these values for things like dance ability, energy, loudness, speechiness, etc. So now that we have an array of audio features, we need to associate those audio features with each track somehow so we can sort the tracks by audio feature.
[10:54 - 11:08] The way we're going to do that is by updating our memoized array here, tracks for track list. So here I'm going to update the tracks for track list array to be called tracks with audio features.
[11:09 - 11:42] And here we're just mapping over the tracks array and we're finding the corresponding audio features object from the audio features state variable using the tracks ID and assigning it to the tracks audio features property. So with this tracks with audio features array, if we console log that and pass it to our track list component.
[11:43 - 11:59] We'll see that the array that's being passed to the track list component now has a audio features property on each track object. And we're going to use these values in order to sort the tracks by audio feature.
[12:00 - 12:10] So to do that, we're going to do three things. We're going to add a state variable called sort value that keeps track of the audio feature being sorted by.
[12:11 - 12:32] We're going to create a drop down with a select element to let the user choose which audio feature to sort by. And then we're going to create another memoized array called sort tracks that sorts our tracks with audio features array here, according to the currently selected sort value.
[12:33 - 12:46] So up here, I'm going to add another use state variable called sort value, initialize it as a empty string at first. And then here I'm going to add an array called sort options.
[12:47 - 13:02] And these are just the audio feature values that we're going to sort by. And then down here, we're going to add a select element that's going to be a drop down that uses that sort options array.
[13:03 - 13:13] It maps through it and then it exposes these sort values as options. And we add a on change handler to this select element.
[13:14 - 13:21] So on change, it just gets the value of each option. So in this case, it's Dancibility, Tempo, or Energy.
[13:22 - 13:38] And then it uses the set sort value, use state function to set the current sorted value. Now we can come back up here and underneath our tracks with audio features, memoized array, we're going to create another one called sorted tracks.
[13:39 - 13:51] So this sorted track memoized array uses the sort value and tracks with audio features as dependencies. And it basically just sorts them using the sort value.
[13:52 - 14:08] And for example, with Dancibility, since these are numbers, we can compare them with a minus operator here. And the more Dancible it is, the closer to one it'll be and the less Dancible it is, it'll be closer to zero.
[14:09 - 14:35] We'll make sure to go down to our track list and replace tracks with audio features here with sorted tracks. So if we look at our playlist page now, if we update our drop down here, it looks like it's working and on change, it's going to update the sort value, which triggers this use memo function to run again.
[14:36 - 14:47] And it basically just sorts the tracks in this case with the highest tempo to the lowest tempo. And in this case, the tracks with higher energy will go at the top and the tracks with lower energy will go to the bottom.
[14:48 - 14:55] Awesome. So the last thing we're going to do for our playlist page is add some styles to this drop down.
[14:56 - 15:10] So in our styles directory, we're going to add a style component called style drop down. And in here, it's basically going to absolutely position the drop down to the upper right hand corner.
[15:11 - 15:26] And it's just going to give the select element some styles. Then we'll make sure to export it from our index file.
[15:27 - 15:40] And then in our playlist JS file, you can add it to our list of imports. And then down here, we'll update our div to style drop down.
[15:41 - 15:57] And if we take a look at the select element here, we're expecting an active prop from our style drop down style component. And if that active prop is true, then we set the background color to a semi transparent white.
[15:58 - 16:06] And if not, it's just transparent. So we're just expecting this to have an active state if it's not in its initial state.
[16:07 - 16:21] So we'll come here and we'll just pass an active prop to our style drop down. And we'll use a double bank here to make sure we're passing a truthy or a fuzzy value of sort value here.
[16:22 - 16:33] So if sort value is not an empty string, then we'll add a background color to this select. Great.
[16:34 - 16:48] So we're almost done building out our app. And the next module will button things up by adding a loading animation.