Show what you're listening to on Spotify — as an embeddable SVG card for your GitHub README.
- Go to Spotify Developer Dashboard
- Create a new app
- Set the redirect URI to your ngrok or Vercel URL +
/api/spotify/callback - Copy the Client ID and Client Secret
- Create a project at supabase.com
- Go to SQL Editor and run the contents of
src/supabase/seed.sql - Copy your Project URL and Service Role Key from Settings > API
cp .env.example .env.localFill in your values:
SPOTIFY_CLIENT_ID=
SPOTIFY_CLIENT_SECRET=
SPOTIFY_REDIRECT_URI=https://your-ngrok-url.ngrok-free.dev/api/spotify/callback
NEXT_PUBLIC_BASE_URL=https://your-ngrok-url.ngrok-free.dev
SUPABASE_URL=
SUPABASE_SERVICE_ROLE_KEY=
CRON_SECRET=
Local development: Spotify OAuth requires a publicly accessible callback URL. Use ngrok to expose your local server:
ngrok http 3000Then use the ngrok URL for
SPOTIFY_REDIRECT_URIandNEXT_PUBLIC_BASE_URL.Production: Replace with your Vercel deployment URL (e.g.
https://your-app.vercel.app).
bun install
bun devDeploy to Vercel:
- Push your repo to GitHub
- Import the project on Vercel
- Add the environment variables — replace the ngrok URLs with your Vercel domain:
SPOTIFY_REDIRECT_URI=https://your-app.vercel.app/api/spotify/callback NEXT_PUBLIC_BASE_URL=https://your-app.vercel.app - Update the redirect URI in your Spotify Developer Dashboard to match
- Visit
/connecton your deployed app - Click Connect Spotify and authorize
- After connecting, you'll be redirected to your dashboard with an embeddable markdown snippet
- Copy the snippet and paste it into your GitHub README
Add this to your README (replace YOUR_PUBLIC_ID with the ID from your dashboard):
The card updates automatically — it shows what you're currently listening to on Spotify with a 15-second cache.
The card supports color themes via a ?theme= query param. Append it to the embed URL:
| Theme | Description |
|---|---|
default |
Warm gold/cream (used when no theme is given) |
onedark |
Atom One Dark — slate bg, blue accent |
dracula |
Dracula — dark purple-grey, purple accent |
nord |
Nord — cool blue-grey, frost accent |
gruvbox |
Gruvbox — retro brown, yellow accent |
Unknown or missing theme names fall back to default, so the embed never breaks.
On your dashboard you can pick a theme from the Theme chips — the live preview and the
copyable snippet update instantly, and your choice is remembered (saved to localStorage).
Themes are defined in src/lib/svg-themes.ts; add a palette to the
THEMES registry to create a new one.
Supabase free-tier databases auto-pause after 7 days of inactivity. To prevent this, a daily cron job pings the database.
- A
pingtable stores a single row with apinged_attimestamp - Vercel Cron calls
GET /api/cron/pingon a schedule (daily by default) - The endpoint upserts the ping row, keeping the database active
- The endpoint is protected by
CRON_SECRETso only Vercel can call it
- Generate a secret:
openssl rand -hex 32
- Add
CRON_SECRETto your Vercel environment variables with the generated value - Create the
pingtable by running this in Supabase SQL Editor:create table ping ( id integer primary key default 1, pinged_at timestamptz not null default now(), constraint single_row check (id = 1) ); insert into ping (id, pinged_at) values (1, now());
- After deploying, verify the cron job appears in Vercel dashboard under Cron Jobs
Edit the schedule in vercel.json:
{
"crons": [
{
"path": "/api/cron/ping",
"schedule": "0 0 * * *"
}
]
}| Schedule | Meaning |
|---|---|
0 0 * * * |
Every day at midnight UTC (default) |
0 */12 * * * |
Every 12 hours |
0 0 */3 * * |
Every 3 days |
- Next.js 16
- Supabase (database)
- Tailwind CSS 4
- Bun (runtime & package manager)
MIT