This content originally appeared on DEV Community and was authored by Timy
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.
This content originally appeared on DEV Community and was authored by Timy