React Spotify Blog DemoReact Spotify Blog Source Code
Prerequisites
We’re using Node JS and npm in this project so make sure you have them installed before we get started. I’m also going to be high-level on React, CSS Grid or Flexbox so it’d be good to have some familiarity there before getting started, and I cannot live without ES7 async/await for dealing with asynchronous code like AJAX requests. If any of those terms sound strange to you I cannot recommend the tutorials from Wes Bos highly enough — the smartest and most engaging Canadian I know of!
Finally — if you want to actually be able to press play and hear the music you’ll need a Premium Spotify membership. If you don’t have one that’s OK too — you’ll just have to manually open your playlists in Spotify and open the new one we made.
Planning it out
I’m old school so I always like to start out with a literal piece of paper to plan out exactly what I want something to look like and do. My handwriting is terrible though, so here’s basically what my paper looks like:
I want two main features: First is that I want to be able to write an homage to one of my favorite artists and have a button directly connecting them to a playlist of their albums on Spotify so the reader can start listening immediately. There should also be a way for them to read posts with homages about other artists. Secondly, I think it’s criminal that Spotify doesn’t let you natively shuffle by album (how are you supposed to get into the groove of the artist?) so the user should be able to shuffle their own playlists by album. If they only have a few tracks from an album they should also be able to automatically bring in the other songs from that album.
Another key planning step when using React is to make a list of the components that you’re going to need. Components are small modules of a user interface that can be combined to make the total UI. Not every element has to be its own component, but we want to get the major stuff. Based on my drawing (a rough wireframe) we’ll need about 7 components:
App/Container/Wrapper
Featured Artist Post
Author
Menu to select other posts
Spotify Container
Connection component (when user isn’t logged in yet)
Playlist Chooser
Picking the right tools
Now that I know what I want, the next step is seeing if there are any existing tools or libraries to give me a head start. There are several, so a lot of the work is done for us!
Starting with create-react-app
Let’s get started by cloning create-react-app. They have easy instructions at the repo. If your version of npm is 5.2 or higher you can just open up a terminal, navigate to a new directory for the project and type.
I’m running npm version 4.2.0 so I have to install a create-react-app command line tool (CLI tool) first, then use the CLI tool to create the app. Remember to also do a git init once it’s done so you can save your work.
Start the example up with npm start quick to make sure everything worked OK. If it looks like this then great work! If not, you’re gonna have some problems… I don’t like the file structure that create-react-app gives us, so I like to rework it to put our components in their own subfolder underneath src. That way we can keep helper files that don’t affect the UI directly in src, and separate components in their own folder so they’re easier to browse.
create-react-app default file structure
My preferred file structure
If you do the same, remember to go into index.js and change the import reference for the App component so you index.js can find it in the new components subfolder.
Setting up the structure in
Let’s set up the overall structure first with some dummy components, and then we can drill down individually. We’re just going to repurpose the component as our top-level wrapper. Pull the JSX code out of and replace it with this:
The JSX/HTML code only tells the browser what the elements are, so we need some CSS. We’re going to use CSS Grid to lay out a structure similar to the wireframe. A media query for screens under 800 pixels puts everything into one column for mobile. Let’s just copy this code into App.css for now and the comments explain some highlights.
Let’s see it! Using chrome devtools and highlighting will show you the grid. The reason we did 5 rows instead of 3 is because we want the Spotify player to be able to reach all the way from the header to the footer, but on the left we want featured post to have 2/3 of the vertical space and other posts only 1/3. We probably could have gotten away with 4 rows, but 5 seemed right to me.
Ok! Let’s build out first real display component: First things first — let’s get some pretty components from Material UI (aka Mui). Their getting started page walks us through it. First we’ll need to install their node package.
We’ll also need to add the Roboto font to our index.html so that everything will display properly.
Mui has a great background component called Paper that is similar to Bootstrap panels. They also use a Typography component for headers and subheaders. It looks classy, and I read that using Typography makes it easier to apply their “themes” later if you want to change your look.
Let’s go ahead and create two files in our components folder: FeaturedPost.js and FeaturedPost.css. I usually copy App.js and App.css and just replace ‘App’ with ‘FeaturedPost’ and then edit, but your call. Here’s what those files should look like:
If we’re in the same place, the app should look about like this:
Ok, that’s great. But nothing is more boring that static content. How about we hook some data up to this thing? Data means content, which means we’re ready to set up Cosmic JS!
Getting dynamic content from Cosmic JS
Cosmic JS is a backend content management system that gives us the best of app development and content. On the content side we get all the convenience of a Wordpress-like interface that a marketing team or blogger can use to easily add new posts, authors or really anything they would like. I actually find it even easier that Wordpress because it’s cleaner and simpler. But on the app development side we get a very clean, simple json-API that we can feed into our crisp React app (or any other framework you like). We don’t have to deal with php and plugins and all the clutter that Wordpress forces on us and that bloats websites. Yay!
First we create a new free account at Cosmic JS. You’ll have to verify your email or you can use GitHub to sign in. I recommend using GitHub to make deployment easier later.
Once you have your account set up you need to create a new Bucket, which is basically where all of the content data for our app will go. You’ll use a unique name for the bucket (I used oldschoolshuffle). Select Start from Scratch and save the Bucket.
Inside a Bucket, data is organized into Objects, Object Types, and Files. It’s a very lightweight and flexible system. What are each of these?
You can also set up multiple Users, but User in this sense is people who have permission to change things in Cosmic JS (like you as a developer, or a blog post author), not users in your app.
We’ll set up our Object Type for Authors first, since we want everyone to know who loves old-school listening. Based on our wireframe an Author needs a name and an avatar image so we have a little more flair. Click on Add Object Type in the left-hand menu, then under Basic Settings let’s do Author for the singular name and Authors for the plural name.
Next click on the Metafields Template tab. Every Object Type gets a title and a content Metafield automatically, so we don’t have to add a title Metafield, but we do need to add a Metafield for the avatar. Click on the blue plus sign and you’re presented with a menu of different types of Metafields. We want an image, so choose that.
Remember, we aren’t making a specific Author yet so resist the temptation to drag and drop an image unless you want to set a default. What’s more important is that we set the title to avatarImage so we can assign things to it later. I like camelCaseToWriteThingsThatIllHaveToUseInMyCodeLater but you don’t have to in this case because Cosmic JS will automatically assign a key that we’ll use in the code.
Once you’re done, click the Save Object Type button. Now we can create our two specific Authors. You should now see a new Authors folder under the left-hand menu. Click on that and you should have a blank section and an Add Author button. I recommend you click that button.
Nice work! Put the author’s name (I used Elvis Costello) as the title. You’ll also see that our avatarImage Metafield that we made in the Object Type carried over, and now we can choose an image, such as an old-school RV trailer, to use as the avatar.
When we’re done push the Publish button to save this Author and close the screen.
Do the same for a second Author, just for fun. Then we need to build the Object Type for our Posts by clicking on Add Object Type from the menu again. Put Post/Posts for singular/plural and go the the Metafields Template tab.
We’re adding two Metafields for Posts: spotifyArtistId and author. The first, spotifyArtistId, is going to be a Plain Text Input, and that’s where we’re going to store a unique Id that Spotify uses to identify musical artists in their API.
The second Metafield (author) is going to be a Single Object Relationship Metafield, which means that we are going to connect Posts with a SINGLE Author of the Object Type we created earlier.
Make sure for the author metafield you choose Limit Search by Object Type and choose Authors from the dropdown menu. It doesn’t make sense for a Post to be the author of a Post. Once that’s done click Save Object Type and we’ll return to the dashboard.
Pro tip! If you made Posts before Authors (jumping the gun!) you can go back and EDIT your Post Object to add the author Metafield. Choose Posts from the menu and clicking the gear icon. Took me a while to figure that one out!
Ok, now we just need to make a few posts and then we’ll be done with the initial content setup! Click on Posts, then App Post, and let’s create 3. Use the artist name for the title, put your homage to them in the content section (go ahead, I’ll wait) and choose an Author from the dropdown menu for each. Note that the content area is HTML-enabled, so you can get a little creative in there. You’ll also need the spotifyArtistId, which looks something like this: 70kkdajctXSbqSMJbQO424. You can look up the ids for your favorite artists here or you can just use these:
WE’RE FINALLY DONE WITH CONTENT! What a brain workout. Let’s go back to something simple like coding mobile-response single page applications with third-party data.
Wiring up Cosmic JS Content
First off: if you aren’t comfortable with React you should stop reading now and go check out Wes Bos’ React for Beginners course and you will literally come out as an expert. Like we did with the Grid CSS stuff I’m going to try and stay top level.
Sound good? Ok, let’s start where all good things in React start — at our top-level component. In React everything is about state (internal conditions in a component) and props (data or functions that get passed down to child components). Let’s add a constructor to .
The properties in this.state are everything that we’re going to need from Cosmic JS to make the blog/homage part of this work:
Now we need to connect our React app to our Cosmic JS API and get the data that we entered back in the CMS. React components really shouldn’t do too much heavy lifting, so for the sake of being modular and readable go to your src folder and add a new file called cosmicFunctions.js (this will be outside of the components directory).
Cosmic JS offers a really handy node module on npm that makes it simple to get the data we need without dealing with CORS (cross-origin-request) issues or ajax requests. Let’s install that with npm first.
We also need to add a .env file with the name of our Bucket (also called a slug). You don’t want to expose that sensitive data in your code, so we store it in an environmental variable that you don’t upload to your public repository. Fortunately create-react-app makes that easy, so you just need to create a .env file in your root directory. Make sure it’s in the ROOT directory, NOT in your src directory, and that it starts with a dot. We also need to start the variable name with REACT_APP_ otherwise create-react-app will ignore it.
If you don’t know your Bucket slug click on Settings -> Basic Settings in your Cosmic JS dashboard and you’ll see it in the second line.
Now that we’re set up we’ll import the Cosmic JS node module into our new cosmicFunctions.js file and add 2 functions getCosmicJsData and organizeCosmicJsDataByObjectType.
Let’s look at getCosmicJsData. First you’ll notice export async before we define the function. export lets us use this function later in . async enables us to use asynchronous methods within the function, in our case an ajax API request to Cosmic JS. Async means that we send the API a request, go about our business, and when we get a response back then our app will return to the function and put the data to use.
This async stuff used to be a real pain in the butt but now we can simply put the keyword await before any asynchronous method and the code will automatically wait for a response with useful data before continuing. It’s awesome.
Let’s look closer at the 10th line:
Wow, lot’s going on there. Here’s the logic: bucket.getObjects() is an async method that tells Cosmic JS to send us a payload of everything they have. Because we don’t have much data, it’s easiest just to get everything from the API now (when the page first loads) and sort it ourselves. As is common with APIs, a lot of that payload is metadata we don’t need. So, we wrap that call in parenthesis and just take it’s objects property, which is an array of all the Posts and Authors that we have.
Now we have the data we need as a jumble of all our Object Types, but to store it cleanly in our state we want to be more organized. That’s why we pass arrayOfAllObjectsInBucket to the organizeCosmicJsDataByObjectType function. It maps through the array and returns an object with keys based on the different Object Types and arrays of the Objects of that type. We could have just looked for Posts and Authors, but this way is more flexible and wouldn’t need to be changed later if we decided to rename Posts to HomagesToGreatness, or something like that.
Anyway, that transformed data looks like this:
See how nicely that will feed in our state in ? Let’s hook it up! React components have built in lifecycle methods where we can tell it what to do at loading, closing, rerendering, etc. The best time to get outside data into a component is after it is first mounted into the DOM (if you don’t know what the DOM is your should probably just leave now) so let’s add a componentDidMount method in and put our getCosmicJsData function in it.
A few key points on this code:
Let’s also check out this gnarly line:
What’s going on here? Short answer is it tells to not render until after we receive the data we need. React calls this conditional rendering, and it prevents ugly blank components or errors.
In practice, it’s just a ternary operator that passes the JSX React component if this.state.dataReceived is true, or renders an empty string if not. Since we update this.state.dataReceived in componentDidMount React now knows when it’s safe to show the FeaturedPost.
Last scary-looking thing:
So is just the React component we made earlier. It needs the data in a Post object to display the artist name and the homage. this.state.posts is the array of Post objects we got from Cosmic JS.
this.state.featurePostIndex is just an array index to pick which post in the array we’re interested in, like posts[0]. So all we’re doing is identifying a Post object and passing it to FeaturedPost as a property (props) called post. That way we can use the data in the child component, even though we got the data in the parent component!
Let’s move on to and actually display the data.
Displaying our homage to Kacey Musgraves
Let’s just go straight to the code and dive in here. We’re going to modify the code we wrote earlier to pull in the data from the post property that we just passed down.
We’re not going to tackle the Play Discography button yet, but we replaced the artist Cake with {this.props.post.title} so now we get dynamic post titles/artist names. Content we also replaced with dynamic content, but it’s more complex because the content Metafield from Cosmic JS is HTML, not just simple text.
React is very skeptical about directly injecting HTML from outside sources into your app because it’s a significant security risk, but we’re ok with it because we control our Cosmic JS data and because this is just an example application. To utilize outside HTML you have to use the dangerouslySetInnerHTML property on a div element and you also have to pass it a uniquely formatted object, not just the HTML code as a regular string. Basically they make it cumbersome so you don’t do it by mistake.
We also see a new component, which passes along the author name and image too. As was given to you by your parent, so you shall pass on to your own child one day.
is pretty straightforward so we’ll just copy over the code for that. Remember you’ll need to create files in your components directory for Author.js and Author.css. and remember to import Author.js at the top of FeaturedPost.js.
Do an npm start and see what it looks like. Hopefully something about like this?
Boom shaka laka. Nearly done with the content stuff! Unfortunately is probably the trickiest part of the content stuff.
Let’s do it in 2 parts. Before we get dynamic with it, let’s lay out a basic dumb component using Material UI’s Expansion panel component. This means making OtherPosts.js and OtherPosts.css files in our component directory. We’ll also need to go into our component and replace our placeholder text with the newly created dummy component. Here’s the code:
Lots of code, but nothing too scary. The expansion panel stuff looks strange, but it’s basically the same structure as a
That’s not too bad. Using {postObject.title} to feed in the post names is simple. The only surprising part is that key property, where we’re incorporating the array index, which wasn’t in our dummy component. The reason for that is that React uses the key property to differentiate between similar elements with the same parent (like
A few changes to note here:
Added changeFeaturedPost method: This is the method we’ll pass down to . can then call the method using the proper index for the Post. That will in turn change the state.featuredPostIndex in . Since passes state.featuredPostIndex to then will update. Simple as pie!
Passed allPosts to: is going to need the array of all Posts so it can determine the index of whatever post is selected. We’ll explain this is more detail in just a minute.
Passed the changedFeaturedPost method to: By passing the function itself, not the state, we enable to call it. Remember to pass it as an arrow function instead of just {this.changedFeaturedPost} otherwise React will get confused and clicking a Post will return the function itself, not the RESULT of the function.
Now let’s update
The rubber finally hits the road with that nice handleClick method! Here’s what’s happening:
First we have to pass the handleClick method into the onClick method for the Post. Remember onClick is an event listener so you have to pass the event even if we aren’t using it.
This method is called when a Post is clicked in , and receives the postObject of the Post that is clicked.
To call the changeFeaturedPost method we passed down from we actually need to look through the array of all the Posts (which is why we passed it down from as props.allPosts) and return the INDEX that matches the Post that was just clicked. Javascript has a great array.findIndex function where we can define what object properties (in our case that we’re looking for and return the index.
Once we have that index, we simply call our passed-down changeFeaturedPost method with it and voila — React does the rest!
If everything went well we should now be able to click on a post, see the featured post change to what was clicked and our list of other posts adjusted.
What do I need the API to do?
Get access to a user’s Spotify
Get their playlists
Get the tracks for a specific playlist
Get the albums for a specific playlist
Get the tracks for a specific album
Create a playlist
Shuffle a playlist
Play discography for a specific artist
Get the albums for an artist
Is any of this work already done and available to build on top of?
Yes! The excellent spotify-web-api-js wrapper library by JMPerez!
Registering your app with Spotify
While we’re getting things set up, let’s go ahead and install spotify-web-api-js with npm, as well as some specific Lodash helper functions we’ll be using.
Let’s also create a spotifyFunctions.js file in the src directory (next to cosmicFunctions.js). It’s gonna have a LOT of code in it though, so let’s just import our libraries for now and then talk about how Spotify’s authentication works.
Authentication
We won’t spend much time on these components: in we replaced our dummy Spotify placeholder text with . has state for whether we are logged in and, if so, what our access token from Spotify is. If state.loggedInToSpotify is false then we show the component (basically just a log in button), or shows a placeholder with the access token.
Let’s start with logging in. redirectUrlToSpotifyForLogin is just a string concatenator to build a URL with query string to tell Spotify who our app is and what permissions (Spotify calls them scopes) we want to ask the user for. It also includes our Client Id and Redirect URI that we registered with Spotify. You can see in that we simply link our login button to this generated URL.
FYI getHashParams is just a generic helper function to parse query strings from the URL.Now we have everything in place we need to log in to Spotify. Here’s what it should look like.
After Log in
You’ll notice a lot of references to our spotifyFunctions.js helper library in there, so it won’t render until we tackle that part. That is where I want to focus next.
Meet Mr. Spot: A iffy API
As a quick recap, here’s what we want the API part of the app to do:
Here’s all the code. Get an overall feel for how it matches up with our requirements, but we’re going to go through each function so don’t worry if you don’t understand everything. I apologize in advance that it could probably stand a little refactoring.
Alright — let’s take it from the top!
We addressed the imports from a little bit ago, but we need an actual instance of the spotify-web-api-js wrapper library to use it, so we’re creating that here and calling it spotifyApi.
These are the authentication functions we addressed earlier. Nothing more to say here.
Once we have our access token we need to pass it to our spotifyApi instance, and then we don’t have to worry about remembering it for later function calls. We have to export this function because it is called in componentDidMount in
Once we have the access token we want to get an array of the user’s playlists that they can choose from. Like all functions that call 3rd-party APIs, this has to be an async function. Our spotifyApi has a built-in getUserPlaylists method but Spotify gives us lots of information about the playlist that we don’t need.
To thin that down we can map through the array of playlist objects we get from the API and return an array of simpler objects with just the id and name of each playlist using ES6 destructuring. We can feed that to state.playlists in and use it to populate our menu.
Notice we also have an error handler that returns “Can’t Download your Playlists” if it can’t connect to the API. That feeds into the menu too, and so the user gets a somewhat helpful message instead of a crashed app if there are connection issues.
Once a user chooses a playlist we need to find out what tracks are on that playlist. We need the name and id of the track, the name and id of the album it’s on, what track number it is on that album, and the name and id of the artist that sings that song. Spotify also saves a URI we can just drop into a browser to get the track, so we’ll save that too.
This is very similar to getSimplePlaylistTracks, but instead of receiving a playlistId we pass it an albumId, albumName and albumUri. This is helpful for when the user wants to add discography because we can loop through their playlists, get the albums for each track, and then use this to get all the other tracks from the same album.
Both functions look very similar, but are subtly different because the Spotify API responses for albums are structured slightly differently than their data on playlists. There’s probably there is a clever way to refactor them into one function — if you come up with it let me know!
Just like we said in the last section, this function receives the output of getSimplePlaylistTracks and produces an array of albumIds from the tracks in that playlists. That can be fed into getSimpleAlbumTracks if we need the full discography or just return the albumId if we just need to sort a playlist by album. The returnSimpleArray flag lets us decide which kind of output we want.
Notice that we get the array of albumIds first, then remove duplicates from the array using the Lodash uniq function, then shuffle it. It’s more efficient to shuffle the albums first before we add tracks to them.
This is just a general helper function to shuffle arrays randomly.
This is basically the same as identifyAlbumsInPlaylist, except it uses the artistId. We’ll use this for playing the discography of artists profiled in . Once again we could probably refactor these two functions together.
This is a tricky one. Essentially we’re taking a large array of tracks in a playlist and converting it from a cohesive array to an object with smaller arrays with the same albumId. Those short arrays are albums, so then within each album we sort tracks by their track number on the album. Later we can recombine them.
Here’s an example of what the effect of convertPlaylistToObjectByProperty looks like:
Cool. Here’s a quick one:
This is a great helper function to sort and array of objects based on a specified property of the objects within it when you feed it as a parameter to array.sort like we did in convertPlaylistToPbjectByProperty . I unabashedly lifted this from Stack Overflow, and owe Ege Ozcan a debt of gratitude!
Look at that export statement! Look at how readable that is! This function is called from the Play Now button in when a user chooses to shuffle a playlist by album WITHOUT adding missing tracks. It is an async function, and we’re really benefiting now from abstracting out our lower-level operations.
We start by receiving the state from and pull out the name and id of the playlist chosen by the user. From there we:
I know we haven’t tackled createPlaylist yet — saving that for last.
This one is very similar to our last function, but byAlbumWithDiscography has different logic in the middle. Once we have the shuffled array of albums from the user’s playlist we throw the actual playlist away. Instead we loop through the array of albumIds and do an API call to get the tracks for each one, which we then combine into one big playlist.
In ES7 Javascript added the async/await keywords, but under the hood it’s still using Promises. When we make an asynchronous API call within an array.map function we actually return an array of unfulfilled promises, not the result of those promises. Confusingly, even though it is full of unfulfilled promises, the array itself is created synchronously which means that awaiting promiseArrayOfTracksFromAlbum will not work. Instead, we call await Promise.all(promiseArrayOfTracksFromAlbum), which creates a new promise that resolves once all the promises in the array are resolved.
This is the function we use to create a playlist of the entire discography of the artist displayed in . It is almost exactly the same as byAlbumWithDiscography except instead of getting the array of albumIds from a playlist we get it by asking the SpotifyAPI for albums associated with the artist. While we’re at it, let’s go ahead and wire up this function with the Play Discography button in :
The final spotifyFunction — createPlaylist
Well, Spotify’s API doesn’t make it easy on us. Even though we have the seemingly handy spotifyApi.createPlaylist and a spotifyApi.play functions, they come with some conditions:
Whoops — Lessons from real life — API setbackI know, you were so excited to be done, and I was too. But Spotify had other plans. Long story short between when I started this project and when the blogs were publish Spotify announced changes to their API and then reneged on those changes, thereby breaking our app! All they did was change the endpoint for creating a playlist, but that means that our spotofy-web-api-js library doesn’t work! If I were a better citizen I’d do a pull request on the library to fix it, but the quick fix is to write our out temporary ajax API call for creating playlists for now instead of the library. Once the library is updated we’ll change it back.
I didn’t copy the whole file to the gist since we’re only changing a few things:
Now that we need our access token outside of the spotify-web-api-js library we have to add a global variable to hold it, as well as update setAccessToken
We need to call our tempCreatePlayist function inside our createPlaylist function that we wrote above.
We need to make our temporary function, which is just an ajax call to the endpoint we need.
It Works!
Now that we have a server to host our build files we need a script to use it. Open up your package.json and let’s do that.
So close we can taste it! Cosmic JS makes it extremely easy to deploy straight from your Github repo (you have been uploading your git commits, right?). All you have to do is go back to you Cosmic JS Dashboard and there is a Web Deployment option underneath settings. From there you simply click the blue Deploy to Cosmic JS button. It will take a few minutes to get set up and send you an email when you production Old School Shuffle is live.
Final touches
That’s all folks!
And now enjoy the fruits of your labor! Thanks for joining me, and I hope you enjoyed it and learned something. This is my inaugural coding tutorial so please leave a comment if I missed anything… or if you have any comments!