A pixel-perfect, fully responsive Spotify web application clone built with modern React ecosystem technologies. This project aims to recreate the complete Spotify web experience with real API integration and professional-grade code architecture.
Currently Under Active Development
The project has a solid foundation with 80%+ of the UI completed for both mobile and desktop layouts. Core features including OAuth authentication, custom Mock Auth system, fully functional media player, and User Profile API are implemented and working. Due to ongoing maintenance on the Spotify Web API, remaining integrations (Search, Playlist CRUD operations) will be implemented once the API becomes available again.
This project implements two separate authentication flows for learning and demonstration purposes:
π― Purpose: Real authentication to obtain access tokens from Spotify API for fetching user data and (future) music content.
- Complete OAuth Flow - Authorization code flow with PKCE for enhanced security
- Code Verifier & Challenge Generation - Cryptographic functions using Web Crypto API (SHA-256, Base64URL encoding)
- Token Exchange - Secure token exchange with Spotify's token endpoint
- Auto Token Injection - Axios interceptors automatically attach Bearer tokens to API requests
- Persistent Sessions - Token storage with localStorage for session persistence
π Purpose: Built from scratch to understand JWT authentication flow, token management, and backend simulation.
β οΈ Note: This is purely for educational purposes and has no effect on the rest of the application.
- Custom JWT-like Auth System - Complete authentication API implemented from scratch using Mock Service Worker
- User Registration - Full signup flow with form validation (email, password, username)
- Login/Logout Flow - Session management with mock backend responses
- Token Generation & Validation - Simulated JWT token handling
- MSW Handlers - Custom request handlers mimicking real backend behavior
- Real API Integration - Fetching user data from Spotify's
/meendpoint - Profile Display - Username, email, follower count, country, subscription type
The media player is a complete implementation with both desktop and mobile variants:
- HTML5 Audio API - Native browser audio playback with full control
- Custom useMediaPlayer Hook - Centralized audio state management with TypeScript interfaces
- Redux State Management - Playing status, timestamps, volume, and mute state stored in Redux
- Desktop Layout - Three-column layout with header, left sidebar, main content, and right sidebar
- Mobile Layout - Single column with bottom navigation bar
- Header - Navigation, search bar, user controls, and premium/download CTAs
- Left Sidebar - Collapsible library with playlist filtering (Playlists, Artists, Albums)
- Bottom Navigation - Mobile-optimized navigation with popover hints
- Footer - Spotify-styled footer with navigation links and social media icons
- Tab Navigation - All, Music, Podcasts tabs with Radix UI Tabs
- Content Carousels - Horizontal scrollable lists with Embla Carousel
- Playlist Cards - Interactive cards with hover play button animation
- Responsive Grid - Adaptive card sizing for different screen sizes
- Category Grid - Colorful genre/category cards with dynamic backgrounds
- Search Input - Spotify-styled search bar component
- Recent Searches - Search history page
- Mock Genre API - MSW handlers serving genre data
- Playlist Header - Cover image, title, description, owner info
- Track List - Song items with artwork, title, artist
- Action Buttons - Like, Share, More options with icon buttons
- Download Page - App download promotion page with gradient background
- Register Page - User registration form with validation
- User Profile Page - Account information display
Implemented Spotify's "Encore" design system with custom CSS variables:
/* Color Tokens */
--color-spotify-green: #1ed760;
--color-encore-background-base: #121212;
--color-encore-background-highlight: #1f1f1f;
--color-encore-background-elevated-base: #1f1f1f;
--color-encore-text-base: #ffffff;
--color-encore-text-subdued: #b3b3b3;
/* Typography Scale (10 sizes) */
--text-smaller-2: 0.6875rem; /* 11px */
--text-base: 1rem; /* 16px */
--text-larger-5: 3rem; /* 48px */
/* Spacing Scale */
--spacing-tighter-5: 2px;
--spacing-looser-6: 64px;
/* Border Radius */
--radius-rounded: 9999px;- Button - 8 variants (default, destructive, outline, secondary, ghost, link, spotify, social)
- TextInput - Form input with error states
- FormField - Label and error message wrapper
- Carousel - Horizontal/vertical carousel with navigation controls
- PlayButton - Spotify-styled circular play button
- BackButton - Navigation back button
- ListCard - Content card with hover effects
Redux Toolkit for client state:
// Configured slices
const rootReducer = {
fakeAuth: fakeAuthReducer, // Mock auth state
auth: oAuthReducer, // OAuth token state
currentUserProfile: currentUserProfileReducer, // User data
mediaPlayer: mediaPlayerReducer, // Player state
};TanStack Query for server state:
- Query Factory Pattern - Organized query definitions with
queryOptions() - Error Boundaries Integration -
throwOnErrorfor ErrorBoundary fallbacks - Query Cache Error Handling - Centralized error logging and toast notifications
// Query factory example
export const userQueries = {
all: () =>
queryOptions({
queryKey: ['user'] as const,
}),
profile: () =>
queryOptions({
queryKey: [...userQueries.all().queryKey, 'profile'] as const,
queryFn: () =>
fetchWithSchema(CurrentUserProfile, {
url: '/me',
}),
}),
};// Tanstack error handling
export const queryClient = new QueryClient({
defaultOptions: {
queries: {
throwOnError: (_error, query) => {
//if there isn't any data to show user, fallback to ErrorComponent
return typeof query.state.data === 'undefined';
},
},
},
queryCache: new QueryCache({
onError: (error, query) => {
if (typeof query.state.data !== 'undefined') {
//toast notification
toast.error(error.message);
}
},
}),
});Axios Client with Interceptors:
// Auto token injection
apiClient.interceptors.request.use((config) => {
const token = tokenStorage.getAccessToken();
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
});Schema-Validated Fetching:
// Type-safe API calls with Zod
async function fetchWithSchema<T extends z.ZodTypeAny>(
schema: T,
config: AxiosRequestConfig
): Promise<z.infer<T>> {
const response = await apiClient(config);
return schema.parse(response.data);
}- React Hook Form - Performant form state management
- Zod Validation - Schema-based validation with type inference
- Custom Schemas - Reusable validation schemas (email, password, etc.)
- Error Boundaries - React Error Boundary with fallback UI and reset functionality
- Query Error Reset - TanStack Query
QueryErrorResetBoundaryintegration - Form Error Display - Field-level and form-level error messages
- Graceful Degradation - Show cached data on error when available
- MSW (Mock Service Worker) - Full API mocking for development
- Auth handlers (login, register, token validation)
- Genre/Category handlers with mock data
- Automatic activation in development mode
- Path Aliases - Clean imports with
@/prefix - ESLint + Prettier - Code quality and formatting
- TypeScript Strict Mode - Full type safety
- HTTPS Development - Local SSL with vite-plugin-mkcert (required for OAuth)
The application is responsive with dedicated layouts for different screen sizes:
| Breakpoint | Layout Type | Key Features |
|---|---|---|
| < 768px (Mobile) | Single Column | Bottom navigation, compact player, simplified header |
| β₯ 768px (Tablet/Desktop) | Multi-Column | Sidebar navigation, full player bar, expanded controls |
- Touch-optimized interactions
- Swipeable carousels
- Bottom sheet patterns for popovers
- Optimized tap targets
- Hover states and animations
- Keyboard navigation support
- Expanded information display
- Multi-column layouts
| Technology | Version | Purpose |
|---|---|---|
| React | 19.2.0 | UI Library |
| TypeScript | 5.9.3 | Type Safety |
| Vite | 7.3.0 | Build Tool & Dev Server |
| Technology | Version | Purpose |
|---|---|---|
| Redux Toolkit | 2.11.2 | Global State Management |
| React Redux | 9.2.0 | React Bindings for Redux |
| TanStack Query | 5.90.12 | Server State & Caching |
| Axios | 1.13.2 | HTTP Client |
| Technology | Version | Purpose |
|---|---|---|
| Tailwind CSS | 4.1.18 | Utility-First CSS |
| Radix UI | 1.4.3 | Headless UI Components |
| Shadcn/UI | - | Component Library (Radix-based) |
| Technology | Version | Purpose |
|---|---|---|
| React Hook Form | 7.69.0 | Form State Management |
| Zod | 4.2.1 | Schema Validation |
| @hookform/resolvers | 5.2.2 | Zod Integration |
| Technology | Version | Purpose |
|---|---|---|
| React Router DOM | 7.11.0 | Client-Side Routing |
| Technology | Version | Purpose |
|---|---|---|
| MSW | 2.12.4 | API Mocking |
| ESLint | 9.39.1 | Code Linting |
| Prettier | 3.7.4 | Code Formatting |
| Technology | Version | Purpose |
|---|---|---|
| clsx | 2.1.1 | Conditional Classes |
| React Error Boundary | 6.0.0 | Error Handling |
| React Toastify | 11.0.5 | Toast Notifications |
spotify-web-app/
βββ public/
β βββ mockServiceWorker.js # MSW service worker
β βββ music/ # Static audio files
βββ src/
β βββ app/
β β βββ hooks.ts # Typed Redux hooks
β β βββ queryClient.ts # TanStack Query configuration
β β βββ store.ts # Redux store configuration
β βββ assets/
β β βββ fonts/ # Custom fonts
β β βββ icons/ # SVG icon components (30+ icons)
β β βββ images/ # Static images
β β βββ music/ # Audio assets
β βββ components/
β β βββ layout/ # Layout components
β β β βββ Layout.tsx # Main layout wrapper
β β β βββ Header.tsx # Desktop header
β β β βββ LeftSidebar.tsx # Collapsible left sidebar
β β β βββ RightSidebar.tsx# Collapsible right sidebar
β β β βββ Footer.tsx # Footer component
β β β βββ BottomBar.tsx # Mobile bottom bar
β β β βββ BottomNavigation.tsx # Mobile navigation
β β β βββ AuthRequired.tsx# Protected route wrapper
β β β βββ Fallback.tsx # Error fallback
β β βββ ui/ # Reusable UI components
β β βββ Button.tsx # Button with variants
β β βββ Carousel.tsx # Carousel component
β β βββ TextInput.tsx # Form input
β β βββ FormField.tsx # Form field wrapper
β β βββ List.tsx # Content list with carousel
β β βββ ListCard.tsx # Card component
β β βββ PlayButton.tsx # Play button
β β βββ BackButton.tsx # Navigation back button
β βββ constants/
β β βββ constants.ts # App constants
β β βββ routeConstants.ts # Route definitions
β βββ features/ # Feature-based modules
β β βββ auth/
β β β βββ components/ # Auth UI components
β β β βββ hooks/ # Auth hooks (useOAuth, useFakeAuth)
β β β βββ schemas/ # Validation schemas
β β β βββ store/ # Auth Redux slices
β β βββ home/
β β β βββ HomePage.tsx # Home page component
β β β βββ components/ # Home sub-components
β β βββ player/
β β β βββ components/ # Player UI (Desktop, Mobile)
β β β βββ hooks/ # useMediaPlayer hook
β β β βββ store/ # Player Redux slice
β β βββ playlists/
β β β βββ components/ # Playlist components
β β βββ search/
β β β βββ components/ # Search components
β β βββ user/
β β β βββ components/ # User profile components
β β β βββ hooks/ # User hooks
β β β βββ schemas/ # User schemas
β β β βββ store/ # User Redux slice
β β βββ download/ # Download page
β β βββ library/ # Library feature
β β βββ register/ # Registration feature
β βββ hooks/ # Shared custom hooks
β βββ lib/
β β βββ apiClient.ts # Axios configuration
β β βββ mockApiClient.ts # Mock API client
β β βββ utils.ts # Utility functions (cn)
β β βββ schemas/ # Shared Zod schemas
β βββ mocks/
β β βββ browser.ts # MSW browser setup
β β βββ handlers/ # Mock API handlers
β βββ routes/ # Route components
β βββ services/
β β βββ api/ # API query definitions
β β βββ userQueries.ts # User API queries
β β βββ genreQueries.ts # Genre API queries
β β βββ playlistsQueries.ts # Playlist API queries
β βββ store/
β β βββ slices/ # Redux slices
β βββ types/
β β βββ index.ts # Type re-exports
β β βββ api/ # API response types
β β βββ components/ # Component prop types
β β βββ store/ # Redux state types
β βββ utils/
β β βββ getGreeting.ts # Greeting utility
β β βββ tokenStorage.ts # Token management
β βββ App.tsx # Root component with routes
β βββ main.tsx # Application entry point
β βββ input.css # Tailwind & theme configuration
β βββ env.d.ts # Environment type definitions
βββ tests/
β βββ e2e/ # End-to-end tests
β βββ integration/ # Integration tests
β βββ unit/ # Unit tests
βββ package.json
βββ vite.config.ts
βββ tailwind.config.js
βββ tsconfig.json
βββ eslint.config.js
- Node.js 18+
- npm or yarn
- Spotify Developer Account (for API access)
-
Clone the repository
git clone https://github.com/YasirAkbal/spotify-clone-lite.git cd spotify-clone-lite -
Install dependencies
npm install
-
Configure environment variables
Create a
.envfile in the root directory:VITE_SPOTIFY_CLIENT_ID=your_spotify_client_id VITE_SPOTIFY_REDIRECT_URI=https://127.0.0.1:5173/auth/callback VITE_SPOTIFY_TOKEN_URL=https://accounts.spotify.com/api/token VITE_SPOTIFY_AUTH_URL=https://accounts.spotify.com/authorize VITE_SPOTIFY_SCOPE=user-read-private user-read-email
-
Start the development server
npm run dev
-
Open your browser
Navigate to
https://127.0.0.1:5173
| Command | Description |
|---|---|
npm run dev |
Start development server with HTTPS |
npm run build |
Build for production |
npm run preview |
Preview production build |
npm run lint |
Run ESLint |
- Complete responsive layout (Mobile + Desktop)
- Implement all major UI components
- Create design system with Spotify theme
- Build media player with full controls
- Implement Spotify OAuth 2.0 with PKCE
- User profile API integration
- Protected routes
- Search functionality with Spotify API
- Playlist CRUD operations
- Optimistic Updates - Immediate UI feedback for mutations
- Infinite Scroll - Virtualized lists for large datasets
- Skeleton UI - Loading state placeholders
- Unit tests with Vitest
- Integration tests
- E2E tests with Playwright
- Multi-language support (i18n)
- Monitoring and logging integration
- WCAG 2.1 accessibility compliance
- Performance optimization
This project is for educational purposes only. Spotify and its logo are registered trademarks of Spotify AB.
Disclaimer: This project is an educational clone of Spotify built for professional learning purposes. It is not affiliated with Spotify. All content is accessed through the public Spotify Web API.






