🎧 Why Add a Spotify Widget?
A portfolio site doesn’t have to be just another static page — small personal details can make it stand out.
A Spotify recently played widget does exactly that:
- It makes your portfolio feel alive — showing what you’re listening to right now.
- It adds personality. Employers, clients, or fellow developers get a glimpse of your taste in music.
- It’s dynamic. Unlike static text, the widget updates automatically whenever you play music on Spotify.
Instead of only saying “I build cool things with Astro,” your site also says, “Here’s what I was vibing to last night while coding.”
🛠️ What You’ll Build
By the end of this tutorial, you’ll have a widget that:
✅ Connects to Spotify’s Web API
✅ Fetches your recently played track in real-time
✅ Displays album art, song, and artist info
✅ Runs with a secure Astro server endpoint
⚡ TL;DR
- Get your Spotify API credentials.
- Use Bruno to generate a refresh token.
- Create an Astro API endpoint to fetch data from Spotify.
- Display the widget in your portfolio.
📋 Prerequisites
Make sure you have:
- Node.js and npm installed
- Spotify Developer Account – needed to register your app and get credentials.
- A Bruno API Client (to handle OAuth flow easily)
- Basic understanding of Astro projects
💡 If you’ve never used Bruno before, think of it as a modern alternative to Postman. Lightweight, open-source, and perfect for testing APIs.
🚀 Step 1 — Create a New Astro Project
If you don’t have one already:
npm create astro@latest my-spotify-portfolio
cd my-spotify-portfolio
npm install
Your project structure will look like this:
/
├── src/
│ ├── components/
│ ├── layouts/
│ ├── pages/
│ └── content/
├── public/
└── astro.config.mjs
🔐 Step 2 — Authenticate with Spotify Using Bruno
Spotify requires OAuth 2.0 Authorization Code Flow.
For a portfolio widget, we don’t want users to log in — so we’ll use a refresh token.
Here’s how:
- Open Bruno and create a new request.
- Under Auth → OAuth 2.0 → Authorization Code, fill in:
- Callback URL:
http://localhost:3000/callback
- Authorization URL:
https://accounts.spotify.com/authorize
- Access Token URL:
https://accounts.spotify.com/api/token
- Client ID / Secret: from your Spotify app
- Scope:
user-read-recently-played
- Click Get New Access Token.
Bruno will give you both an
access_token
(short-lived) and arefresh_token
(long-lived).
✅ Save the refresh_token
— we’ll use it in the next step.
🧠 Step 3 — Create an Astro Server Endpoint
Astro’s server endpoints act as backend routes inside your project — no external serverless function required.
First, create a .env
file:
SPOTIFY_CLIENT_ID=your_client_id
SPOTIFY_CLIENT_SECRET=your_client_secret
SPOTIFY_REFRESH_TOKEN=your_refresh_token
Now create your API route:
// src/pages/api/spotify-recently-played.ts
export const prerender = false;
import type { APIRoute } from "astro";
export const GET: APIRoute = async () => {
const client_id = import.meta.env.SPOTIFY_CLIENT_ID!;
const client_secret = import.meta.env.SPOTIFY_CLIENT_SECRET!;
const refresh_token = import.meta.env.SPOTIFY_REFRESH_TOKEN!;
try {
// 1. Get a new access token
const tokenResponse = await fetch("https://accounts.spotify.com/api/token", {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
Authorization: "Basic " + Buffer.from(client_id + ":" + client_secret).toString("base64"),
},
body: new URLSearchParams({
grant_type: "refresh_token",
refresh_token,
}),
});
const tokenData = await tokenResponse.json();
if (!tokenResponse.ok) throw new Error(tokenData.error_description);
const access_token = tokenData.access_token;
// 2. Fetch the recently played track
const recentRes = await fetch(
"https://api.spotify.com/v1/me/player/recently-played?limit=1",
{ headers: { Authorization: `Bearer ${access_token}` } }
);
const recentData = await recentRes.json();
const latest = recentData.items?.[0]?.track;
if (!latest) {
return new Response(JSON.stringify({ error: "No recent track found" }), { status: 404 });
}
// 3. Send a clean JSON response
return new Response(
JSON.stringify({
albumCover: latest.album.images[0]?.url || null,
song: latest.name,
artist: latest.artists.map((a: any) => a.name).join(", "),
spotifyUrl: latest.external_urls.spotify,
}),
{ status: 200, headers: { "Content-Type": "application/json" } }
);
} catch (err: any) {
return new Response(JSON.stringify({ error: err.message }), { status: 500 });
}
};
Now visiting /api/spotify-recently-played
will return your latest played track in JSON.
🎨 Step 4 — Create the Spotify Widget
Finally, let’s build the UI. You can grab a starter widget here:
Copy the code into src/components/SpotifyWidget.astro
and update the fetch URL if needed.
✅ Wrapping Up
You just built a Spotify Recently Played widget using Astro server endpoints and the Spotify Web API.
This small feature shows that you:
- Understand OAuth flows
- Can create secure backend endpoints in Astro
- Know how to integrate third-party APIs into your projects
It’s a small touch — but one that makes your portfolio more alive and personal.
📚 For more details, check out Spotify’s official Authorization Code Flow guide.
Top comments (0)