Packages
@httpjpg/now-playing
Real-time Spotify "Now Playing" widget with color extraction
@httpjpg/now-playing
Real-time Spotify integration showing currently playing track with album art and color extraction.
Installation
pnpm add @httpjpg/now-playingSetup
1. Create Spotify App
- Go to Spotify Developer Dashboard
- Create new app
- Add redirect URI:
http://localhost:3000/api/callback - Note your Client ID and Client Secret
2. Get Refresh Token
Run the token script:
cd packages/now-playing
node scripts/get-spotify-token.cjsFollow the instructions to authorize and get your refresh token.
3. Configure Environment
SPOTIFY_CLIENT_ID=your-client-id
SPOTIFY_CLIENT_SECRET=your-client-secret
SPOTIFY_REFRESH_TOKEN=your-refresh-tokenUsage
Basic Component
import { NowPlaying } from '@httpjpg/now-playing';
export default function Sidebar() {
return <NowPlaying />;
}With Custom Styling
import { NowPlaying } from '@httpjpg/now-playing';
<NowPlaying
className="my-custom-class"
showArtist={true}
showAlbum={true}
/>Hook Usage
useNowPlaying
Access the Spotify data directly:
import { useNowPlaying } from '@httpjpg/now-playing';
export function CustomPlayer() {
const { data, isLoading, error } = useNowPlaying();
if (isLoading) return <div>Loading...</div>;
if (error) return <div>Error loading track</div>;
if (!data?.isPlaying) return <div>Not playing</div>;
return (
<div>
<img src={data.albumImageUrl} alt={data.album} />
<h3>{data.title}</h3>
<p>{data.artist}</p>
</div>
);
}Data Structure
interface NowPlayingData {
isPlaying: boolean;
title: string;
artist: string;
album: string;
albumImageUrl: string;
songUrl: string;
dominantColor?: string; // Extracted from album art
}Features
🎨 Color Extraction
Automatically extracts dominant color from album artwork:
const { data } = useNowPlaying();
// Use extracted color for backgrounds
<div style={{ backgroundColor: data?.dominantColor }}>🔄 Real-time Updates
Data refreshes automatically every 30 seconds.
⚡ Loading States
Built-in loading component:
import { NowPlayingLoading } from '@httpjpg/now-playing';
{isLoading && <NowPlayingLoading />}🎵 Playback Status
Shows whether music is currently playing:
{data?.isPlaying ? (
<span>Now Playing</span>
) : (
<span>Not Playing</span>
)}API Endpoint
The package includes a Next.js API route:
// app/api/now-playing/route.ts
import { GET } from '@httpjpg/now-playing/api';
export { GET };Response:
{
"isPlaying": true,
"title": "Song Title",
"artist": "Artist Name",
"album": "Album Name",
"albumImageUrl": "https://...",
"songUrl": "https://open.spotify.com/track/...",
"dominantColor": "#1DB954"
}Spotify API Functions
getNowPlaying()
import { getNowPlaying } from '@httpjpg/now-playing/spotify-api';
const data = await getNowPlaying();getAccessToken()
import { getAccessToken } from '@httpjpg/now-playing/spotify-api';
const token = await getAccessToken();Color Extraction
extractDominantColor()
import { extractDominantColor } from '@httpjpg/now-playing/extract-color';
const color = await extractDominantColor(imageUrl);
// Returns: "#1DB954"Uses canvas API to extract the most prominent color from album artwork.
Styling Example
import { useNowPlaying } from '@httpjpg/now-playing';
import { css } from '@httpjpg/ui/styled-system/css';
export function StyledPlayer() {
const { data } = useNowPlaying();
if (!data?.isPlaying) return null;
return (
<div className={css({
background: `linear-gradient(to right, ${data.dominantColor}, transparent)`,
padding: '4',
borderRadius: 'lg',
})}>
<img
src={data.albumImageUrl}
alt={data.album}
className={css({ width: '16', height: '16', borderRadius: 'md' })}
/>
<div>
<h4>{data.title}</h4>
<p>{data.artist}</p>
</div>
</div>
);
}Troubleshooting
Token Expired
If you get 401 errors, refresh your token:
node packages/now-playing/scripts/get-spotify-token.cjsCORS Errors
Ensure your Spotify app has correct redirect URIs configured.
No Data
Check that:
- Spotify is actively playing
- Environment variables are set correctly
- Refresh token is valid
Best Practices
- Cache API responses to reduce Spotify API calls
- Handle offline states gracefully
- Use loading states for better UX
- Extract colors sparingly (it's computationally expensive)
- Respect rate limits (use 30s polling interval)