Select a theme. Inspired by Breath of the Wild and Max's awesome color switcher
March 1, 2021

Showing my listening activity

Showing my listening activity

Spotify icon can be found in my Figma Community file (as well as a bunch of other icons)


TLDR: Grab your music and podcast info from Spotify using Next.js


Sticking to this site's ethos - designing in the open - I thought I could share a few new things that I've been finding a ton of fun.

Either while I'm driving, walking, sitting at my desk, or falling asleep, I'm regularly listening to a song or podcast via Spotify. Using their Web API, you're able to access:

  • Music (playlists, artists, songs, albums, etc.)
  • Podcasts (episodes, shows)
  • User metrics (currently listening, top listens)
  • ...and probably a ton more

How to build

After a quick search, I came across Lee Robinson's Spotify & Next.js tutorial. Using the Spotify Web API and Next.js v10+, I threw together a way to grab my own Spotify data and pull it into this site.

Lee's tutorial is incredibly helpful in getting Spotify setup to show your own "Top Tracks", but I also wanted to have a way to show:

  • if I'm currently listening to something
  • if I'm my currently playing item is a podcast or song
  • my last played song
  • my recently subscribed podcasts

I also wanted to design this in a way that was compact and not overly informative. Let's start with a component that let's me see if I'm currently listening to something, and whether that item is a song or podcast. We'll need 2 endpoints for this:

// spotify.js

const NOW_PLAYING_ENDPOINT = `https://api.spotify.com/v1/me/player/currently-playing`;
const NOW_PLAYING_PODCAST_ENDPOINT = `https://api.spotify.com/v1/me/player/currently-playing/?additional_types=episode`;

If you've followed Lee's tutorial and completed the token & auth setup, we just have to add 2 more functions in order to fetch these endpoints:

// spotify.js

export const getNowPlaying = async () => {
  const { access_token } = await getAccessToken();

  return fetch(NOW_PLAYING_ENDPOINT, {
    headers: {
      Authorization: `Bearer ${access_token}`
    }
  });
};

export const getPodcastPlaying = async () => {
  const { access_token } = await getAccessToken();

  return fetch(NOW_PLAYING_PODCAST_ENDPOINT, {
    headers: {
      Authorization: `Bearer ${access_token}`,
    }
  });
};

After that, we just need two routes to grab's playing. I kept the song and podcast routes separate in case I need them later. Using the route for grabbing the currently playing song as an example, we can import one of our new functions to grab the response from Spotify:

// /api/spotify/now-playing.js

import { getNowPlaying } from '@utils/spotify';

export default async (_, res) => {
  const response = await getNowPlaying();

  if (response.status === 204 || response.status > 400 || response.status === 500) {
    return res.status(200).json({ isPlaying: false, playing: null });
  }

  const song = await response.json();

  if(song.context === null) {
    return res.status(200).json({ isPlaying: false, playing: 'podcast' });
  }

  const isPlaying = song.is_playing;
  const title = song.item.name;
  const artist = song.item.artists.map((_artist) => _artist.name).join(', ');
  const album = song.item.album.name;
  const albumImageUrl = song.item.album.images[0].url;
  const songUrl = song.item.external_urls.spotify;

  res.setHeader(
    'Cache-Control',
    'public, s-maxage=60, stale-while-revalidate=30'
  );

  return res.status(200).json({
    album,
    albumImageUrl,
    artist,
    isPlaying,
    songUrl,
    title
    });
};

We would repeat the same function for the currently playing podcast - making slight changes in the object we send back and the endpoint function we're referencing. The above function accounts for 3 scenarios: 1. If a song is playing 2. If a song isn't playing, but a podcast is playing 3. If neither a song nor podcast is playing

Let's see how we can design a single component to account for all 3 of these states:

component states

This component can account for each of the scenarios our API response may give us - helping us only surface the correct information in a way that is a bit more seamless to the user.

What's next?

podcast subscriptions

I've been having some fun grabbing my recent top tracks and my recent podcast subscriptions - check it out! If you have feedback or ideas of what else could be a fun way to make this information more transparent and tangible, I'd love to know - let me know using the form below.


Have feedback?

I'd love to see if this was helpful or if there was anything I should look to update - let me know below!


Hey, I'm Ryan.πŸ‘‹

I'm a designer and have strong opinions on movies 🎬.

More about me→