How to Use the Spotify API with React

How to Use the Spotify API with React

If you love music like I do you have probably thought about creating some sort of application that involves music. In this post, I'd like to talk about what you need to know to get started. I will be going through how to use the API with React, but regardless of what technology you decide to use, you will need to activate your Spotify Developer account for the API access credentials the same way you will here. The API call URLs will also be the same. If you would like to see a full example application using this API, visit my NuFire repo on GitHub. If you would like to use my template to get started using the Spotify API as quickly as possible, feel free to fork my spotify-api-template repo.

Pre-Requisites

For this example you will need all the usual pre-requisites like a code editor, basic terminal/command line knowledge, and have node.js/npm installed.

Spotify Developer Account

First, you will need a Spotify developer account. If you already have a Spotify account, this will use the same login credentials, but you will need to login via the Spotify developer site. If you do not have a Spotify account, you can create a free one. Once logged in, you will be prompted to create an application. This process is very seamless and only takes a few minutes. After creating your first application, you will able to view your applications from your dashboard. Clicking on your application from this screen takes you to the details for that application. Here you can view the Client ID and the Client Secret as well as edit the settings for the application and add users for extended access. Don't worry, the application you see here will have been deleted by the time this post is published. :)

Spotify Developer Dashboard

From this page, click on 'Edit Settings', and add your development URI to the 'Redirect URIs'. This should be something similar to localhost:3000. This tells the API to redirect back to this URI after authentication is finished and a token is acquired, so if you are using a different port than 3000, or if you are actually hosting this application somewhere, this URI will be different. This should be all the setup needed on the Spotify Developer Account side; however, you will need to visit the 'Console' tab on the Spotify Developer site for a list of all the different URL patterns available through the API.

React Application Setup

For this particular case, we are also going to use Axios-HTTP which is a promise based HTTP client for the browser and node.js. Basically, this will replace the 'fetch' syntax that is typically used for this sort of project. This changes our fetch syntax from this:

const baseUrl = 'http://localhost:3000/api/v1'

fetch(`${baseUrl}/drivers`)
    .catch(error => console.log('ok'))
    .then(res => console.log('error'));

to this:

const baseUrl = 'http://localhost:3000/api/v1'

axios.get(`${baseUrl}/drivers`).then(res => res)

We will also be making these calls within a custom context that we will create, and we will use a reducer to manage state based on these calls. So, the first thing we need to do is get our react app created. The easiest way to do this is open the terminal and navigate to where you want this project directory to live and run the command 'npx create-react-app'. Then, we need to install Axios which is the only dependency we have that's not already built-in. So, from the terminal navigate down into that project folder. Now run the command 'npm install axios'.

Now that we have our React application started and our dependencies installed. We can create our first page, our context, provider, and reducer. From there, we can start to make calls to the API and display the data we get from it. First, I always create a folder named 'Pages' in the 'src' directory of the project for all the pages that will need to be created. So in this folder, create a new file name 'Home.js' and this will serve as the landing page of the application. Now, we need to create two more folders in the 'src' directory: one named 'components' and one name 'context'. The 'components' folder will contain the components rendered on the landing page that we use to display data from the API, and the 'context' folder will contain the context and reducer created for state management. We will also need a '.env' file in the main directory to store our keys as environment variables so when we push changes to our repo, our keys are not exposed to the public. If you are using the template, you will still need to create this file as it is ignored by git and not included in the repo. In the ‘components’ folder, you really only need one file. This will be named ‘GenreResults.js’ in the template repo. This component will display all the results from the Spotify API when we request data on all the genres. In the ‘context’ folder, you will need two different files: ‘SpotifyContext.js’ and 'SpotifyReducer.js’. These two files will contain the functions tasked with calling the API, returning the data, and updating the state of the application with that data. So, let's get to coding.

Code It Up

The first thing I like to do is get my environment variables in. So in your '.env' file, add your Client ID, Client Secret, the Spotify Auth Endpoint, and the Spotify API URL. It should look something like this, but make sure to use your own Client ID and Client Secret as this application will be deleted by the time this is posted.

//.env file
REACT_APP_CLIENT_ID = "703b6fbd31b34d77a875cc5b320b2936"
REACT_APP_CLIENT_SECRET = "fb55a4e9dbb14f97a5be3dbe3bc11a6a"
REACT_APP_AUTH_ENDPOINT = "https://accounts.spotify.com/api/token"
REACT_APP_SPOTIFY_API = "https://api.spotify.com/"

Now we can get started on the SpotifyContext and SpotifyReducer. There are tons of tutorials and documents out there on how to use the React useContext hook so I wont go into too much detail here, but your 'SpotifyContext.js' is where you will import and use your environment variables from .env. The variables will then be used to make the calls to the API later on. The context is also where the global state of the application will be stored so that components can access it. The SpotifyReducer will take the data that is received in the response from the API and use that to update the state of the application contained in the context. At first, we will just be getting the API token and updating our application state with that token.

Once the SpotifyContext and SpotifyReducer have been created, the App.js file needs to be updated. The application must be wrapped in the SpotifyProvider component because this is what provides the context to the components in order to be used. You don't necessarily have to do this in the App.js, but wrapping the entire application like this will allow every component in the component tree to access the context. Also, for the purposes of this application I have included the use of React Router. React Router is not mandatory, but this almost always how I set up my apps at the beginning so that I don't have to come back and do it later. The 'App.js' file should look like this.

//App.js
import {BrowserRouter as Router, Route, Routes} from 'react-router-dom';
import {SpotifyProvider} from './context/SpotifyContext';

import './App.css'
import Home from './pages/Home';

function App() {
  return (
    <SpotifyProvider>
      <Router>
        <div>
          <main>
          <Routes>
            <Route path="/" element={<Home />} />
          </Routes>
          </main>
        </div>
      </Router>
    </SpotifyProvider>
  );
}

export default App;

Now we need to update the 'Home.js' file. First, we need to include the context here so that the Home page component can access it via the useContext hook. Then we need to use the useEffect hook in order to call the getToken function in SpotifyContext as soon as the page begins to render. While we are here, we can also go ahead and setup a rough UI to render some data that we receive from the API. For this particular case, I will be calling the API for a list of Spotify Genres, and displaying these in cards in the best way in my opinion so that's how we will do it here. We will display each genre on a card inside a component named 'GenreResults', and that component will be rendered on the 'Home' page component. The data we get from the API will be in an array, and that array will replace the empty array in the 'genres' state variable. So, in order to render all the genres in the list, we will need to loop through them with the .map method. I also added a button that will be used to trigger the 'getGenres' function that we will make in the 'SpotifyContext' file later onThe 'Home.js' file should look similar to this.

//Home.js
import React, {useEffect, useContext} from 'react'
import SpotifyContext from '../context/SpotifyContext'

import './Home.css'
import GenreResults from '../components/GenreResults'

function Home() {
  const {getToken, getGenres} = useContext(SpotifyContext);

  useEffect(() => {
    getToken()
  }, [])

  return (
    <div className="main">
      <div className="nav-bar">
        <button className="action-btn" onClick={getGenres}>
          Get Genres
        </button>
      </div>
      <div className="body">
        <GenreResults />
      </div>
    </div>
  )
}

export default Home

The 'GenreResults.js' file should look like this.

//GenreResults.js
import React, {useContext} from 'react'
import SpotifyContext from '../context/SpotifyContext'

function GenreResults() {
  const {genres, getPlaylists} = useContext(SpotifyContext)

  return (
    <>
      {genres.map((genre) => {
        return (
          <div className="card">
            <img src={genre.icons[0].url} alt="Genre Cover" className="card-image"/>
            <div className="card-body">
              <h2 className="card-title">{genre.name}</h2>
                <button className="link-btn" onClick={getPlaylists}>
                  See Playlists
                </button>
            </div>
          </div>
        )     
      })}
    </>
  )
}

export default GenreResults

Now, at first, this won't actually render anything. In fact, you will get an error saying that getGenres is undefined or something like that. That's totally fine though, we just need to go define it. We will go back into the context and reducer to define our 'getGenres' function that will request data from the API and the 'GET_GENRES' reducer that will update the state of the 'genres' array. I included the base API URL in the environment variables to keep from having to type that repeatedly, other than that, this function will be just like any other API calls with Axios. I do find it helpful to set up the API call and log the response in the console at first to make sure the data is coming in correctly, and to check out what all is actually available within the response data. Your 'SpotifyContext.js' file should now look like this.

//SpotifyContext.js
import { createContext, useReducer } from 'react'
import axios from 'axios'
import spotifyReducer from './SpotifyReducer'

const SpotifyContext = createContext();
const AUTH_ENDPOINT = process.env.REACT_APP_AUTH_ENDPOINT;
const SPOTIFY_API = process.env.REACT_APP_SPOTIFY_API;
const CLIENT_ID = process.env.REACT_APP_CLIENT_ID;
const CLIENT_SECRET = process.env.REACT_APP_CLIENT_SECRET;

export const SpotifyProvider = ({children}) => {
  const initialState = {
    token: '',
    genres: [],
    playlists: [],
  }

  const [state, dispatch] = useReducer(spotifyReducer, initialState);
  const getToken = async () => {
    setLoading();
    const data = await axios(`${AUTH_ENDPOINT}`, {
      headers: {
        'Content-Type' : 'application/x-www-form-urlencoded',
        'Authorization' : 'Basic ' + btoa(CLIENT_ID + ':' + CLIENT_SECRET)
      },
      data: 'grant_type=client_credentials',
      method: 'POST'
    });
    if(data) {
      dispatch({
        type: 'GET_TOKEN',
        payload: data.data.access_token,
      });
    } else {
      console.log('Token not available. Please check Spotify Developer Settings.')
    }
  };

  const getGenres = async () => {
    setLoading();
    const genres = await axios(`${SPOTIFY_API}v1/browse/categories?country=US&offset=0&limit=50`, {
      method: 'GET',
      headers: {
        'Authorization' : 'Bearer ' + state.token
      }
    });
    if(genres) {
      console.log(genres.data.categories.items)
      dispatch({
        type: 'GET_GENRES',
        payload: genres.data.categories.items
      });
    }
  }

  const getPlaylists = async () => {
    console.log('Gotta build out the function first! :)')
  }

  const setLoading = () => dispatch({type: 'SET_LOADING'})

  return <SpotifyContext.Provider value={{
    token: state.token,
    genres: state.genres,
    playlists: state.playlists,
    getToken,
    getGenres,
    getPlaylists,
  }}>
    {children}
  </SpotifyContext.Provider>
};

export default SpotifyContext;

And your 'SpotifyReducer.js' file should now look like this.

//SpotifyReducer.js
const spotifyReducer = (state, action) => {
  switch(action.type) {
    case 'GET_TOKEN':
      return {
        ...state,
        token: action.payload,
        loading: false,
      }
    case 'GET_GENRES':
      return {
        ...state,
        genres: action.payload,
        loading: false,
      }
    case 'SET_LOADING':
      return {
        ...state,
        loading: true,
      }
    default:
      return state
  }
};

export default spotifyReducer;

At this point, you should be able to run 'npm start' in your terminal, click the 'Get Genres' button, and see the first 50 Spotify Genres available to the US market as specified in the API URL used in the 'getGenres' function. Unless of course I missed something in this explanation which is entirely possible. :D The next step would be to build out the 'getPlaylists' function in the SpotifyContext as well as creating the relevant case in SpotifyReducer for updating the state of the playlists array and rendering that data somewhere in the application.

Wrap-Up

Now, you can go in and style everything, explore the response data to see if there is any other information you may want to display for each genre, and create some other functions to make calls of your own. Also, feel free to fork my spotify-api-template repo on GitHub to get a jump start. Technically, you can do this by doing everything inside the ‘Home’ page component and simply skip the creation of the ‘components’ folder. However, this template was made assuming you will eventually turn this into an actual web application of some sort, for which this is the whole point of using the React library. Most importantly, have fun exploring! Also, it's important to note that their are limitations to the Spotify API, as there are with all APIs. Most notably is list length restriction of some of these API calls. For example, calls that return genres, playlists, and albums are limited to 50 items while calls that return a list of tracks are limited to 100 items. It's worth noting this limitation especially if you plan on creating an application that will be used by other people.

Let me know if you learned anything in the comments! :)