Skip to main content

Overview

The SpotifyContext provides centralized state management for Spotify playback, track information, lyrics, and queue management. It handles authentication, playback polling, lyrics fetching from LRCLIB, and automatic prefetching of upcoming track lyrics.

SpotifyProvider

Wraps your application to provide Spotify state and functionality.

Props

children
React.ReactNode
required
The component tree that needs access to Spotify context

Usage

import { SpotifyProvider } from './contexts/SpotifyContext';

function App() {
  return (
    <SpotifyProvider>
      <YourApp />
    </SpotifyProvider>
  );
}

useSpotify Hook

Access Spotify state and controls from any component within the provider.

Return Value

Returns a SpotifyContextType object with the following properties:
sdk
SpotifyApi | null
The Spotify Web API SDK instance for making API calls
currentTrack
SpotifyTrack | null
The currently playing track with full metadata
interface SpotifyTrack {
  id: string;
  name: string;
  type: 'track';
  artists: SpotifyArtist[];
  album: SpotifyAlbum;
  duration_ms: number;
  explicit: boolean;
  uri: string;
  // ... additional fields
}
isPlaying
boolean
Whether playback is currently active
progress
number
Current playback position in milliseconds
duration
number
Total track duration in milliseconds
error
string | null
Error message if Spotify credentials are missing or connection fails
queue
SpotifyTrack[]
Array of upcoming tracks in the playback queue
lyrics
LyricsResponse | null
Parsed lyrics for the current track
interface LyricsResponse {
  error: boolean;
  syncType: string;
  lines: LyricsLine[];
}

interface LyricsLine {
  words: string;
  startTimeMs: string;
  endTimeMs: string;
  timeTag?: string;
}
togglePlayback
() => Promise<void>
Toggles playback between play and pause states
refreshQueue
() => Promise<void>
Manually refreshes the playback queue from Spotify

Usage Examples

Basic Playback Control

import { useSpotify } from './contexts/SpotifyContext';

function PlaybackControls() {
  const { currentTrack, isPlaying, togglePlayback, sdk } = useSpotify();

  return (
    <div>
      <h2>{currentTrack?.name}</h2>
      <p>{currentTrack?.artists[0]?.name}</p>
      
      <button onClick={togglePlayback}>
        {isPlaying ? 'Pause' : 'Play'}
      </button>
      
      <button onClick={() => sdk?.player.skipToNext()}>
        Next Track
      </button>
      
      <button onClick={() => sdk?.player.skipToPrevious()}>
        Previous Track
      </button>
    </div>
  );
}

Display Current Track with Album Art

function NowPlaying() {
  const { currentTrack, progress, duration } = useSpotify();
  
  if (!currentTrack) return null;
  
  const progressPercent = (progress / duration) * 100;
  
  return (
    <div>
      <img 
        src={currentTrack.album.images[0].url} 
        alt={currentTrack.name}
      />
      <h2>{currentTrack.name}</h2>
      <p>{currentTrack.artists.map(a => a.name).join(', ')}</p>
      
      <div className="progress-bar">
        <div style={{ width: `${progressPercent}%` }} />
      </div>
    </div>
  );
}

Access Lyrics

function LyricsViewer() {
  const { lyrics, progress } = useSpotify();
  
  if (!lyrics) return <p>No lyrics available</p>;
  
  const currentLine = lyrics.lines.find(line => {
    const start = parseInt(line.startTimeMs);
    const end = parseInt(line.endTimeMs);
    return progress >= start && progress < end;
  });
  
  return (
    <div>
      {lyrics.lines.map((line, i) => (
        <p 
          key={i}
          className={line === currentLine ? 'active' : ''}
        >
          {line.words}
        </p>
      ))}
    </div>
  );
}

Queue Management

function QueueDisplay() {
  const { queue, refreshQueue, sdk } = useSpotify();
  
  return (
    <div>
      <button onClick={refreshQueue}>Refresh Queue</button>
      
      <ul>
        {queue.map(track => (
          <li key={track.id}>
            <img src={track.album.images[2].url} alt="" />
            <span>{track.name}</span>
            <span>{track.artists[0].name}</span>
          </li>
        ))}
      </ul>
    </div>
  );
}

Seek to Position

function SeekBar() {
  const { progress, duration, sdk } = useSpotify();
  
  const handleSeek = (e: React.ChangeEvent<HTMLInputElement>) => {
    const position = parseInt(e.target.value);
    sdk?.player.seekToPosition(position);
  };
  
  return (
    <input
      type="range"
      min={0}
      max={duration}
      value={progress}
      onChange={handleSeek}
    />
  );
}

Features

Automatic Playback Polling

The context polls Spotify every second to update playback state, ensuring real-time synchronization.

Lyrics Prefetching

When 10 seconds remain in the current track, lyrics for the next queued track are automatically prefetched and cached, providing seamless transitions.

LRC Format Parsing

Lyrics are fetched from LRCLIB in LRC format and parsed into a structured format with precise timestamps for each line.

Caching

Lyrics are cached by artist and track name to minimize API calls and improve performance.

Abort Control

Lyrics fetching uses AbortController to cancel pending requests when tracks change, preventing race conditions.

Environment Variables

The provider requires these environment variables:
VITE_SPOTIFY_CLIENT_ID
string
required
Your Spotify application client ID
VITE_SPOTIFY_REDIRECT_URI
string
required
OAuth redirect URI configured in your Spotify app settings

Spotify API Scopes

The context requests these Spotify permissions:
  • user-read-playback-state - Read current playback state
  • user-modify-playback-state - Control playback (play, pause, skip)
  • user-library-read - Access user’s saved tracks

Error Handling

function ErrorBoundary() {
  const { error } = useSpotify();
  
  if (error) {
    return (
      <div className="error">
        <p>{error}</p>
        <p>Please check your Spotify credentials</p>
      </div>
    );
  }
  
  return <YourApp />;
}

TypeScript Types

All types are exported from /types.ts:
import type { 
  SpotifyTrack, 
  SpotifyArtist, 
  SpotifyAlbum,
  LyricsResponse,
  LyricsLine 
} from './types';

Build docs developers (and LLMs) love