18 KiB
Component Catalog
A comprehensive guide to all reusable React components in the Sonic Cloud platform.
Audio & Playback
AudioPlayer
Purpose: Fixed bottom audio player that displays the currently playing song with full playback controls
Location: components/AudioPlayer.tsx
Props & Usage
Props:
| Prop | Type | Description |
|---|---|---|
| currentSong | Song | Currently playing song object |
| isPlaying | boolean | Playback state flag |
| onPlayPause | () => void | Toggle play/pause handler |
| onNext | () => void | Skip to next track handler |
| onPrevious | () => void | Go to previous track handler |
| progress | number | Playback progress (0-100) |
| volume | number | Volume level (0-100) |
| onSeek | (time: number) => void | Seek to specific time handler |
| onVolumeChange | (level: number) => void | Volume adjustment handler |
Usage Example:
<AudioPlayer
currentSong={currentSong}
isPlaying={isPlaying}
onPlayPause={handlePlayPause}
onNext={handleNext}
onPrevious={handlePrevious}
progress={progress}
volume={volume}
onSeek={handleSeek}
onVolumeChange={setVolume}
/>
Dependencies: PlayerControls, WaveformDisplay Related: PlayerControls, WaveformDisplay
PlayerControls
Purpose: Reusable audio player control buttons for play/pause, skip, shuffle, and repeat
Location: components/PlayerControls.tsx
Props & Usage
Props:
| Prop | Type | Description |
|---|---|---|
| isPlaying | boolean | Current playback state |
| onPlayPause | () => void | Toggle play/pause |
| onNext | () => void | Skip to next track |
| onPrevious | () => void | Go to previous track |
| shuffle | boolean | Shuffle mode enabled |
| repeat | 'none' | 'all' | 'one' | Repeat mode |
| onShuffleToggle | () => void | Toggle shuffle |
| onRepeatToggle | () => void | Cycle repeat modes |
| size? | 'sm' | 'md' | 'lg' | Button size variant |
Usage Example:
<PlayerControls
isPlaying={isPlaying}
onPlayPause={togglePlayback}
onNext={skipNext}
onPrevious={skipPrevious}
shuffle={shuffleEnabled}
repeat={repeatMode}
onShuffleToggle={toggleShuffle}
onRepeatToggle={cycleRepeat}
size="md"
/>
Dependencies: None (presentational component) Related: AudioPlayer
WaveformDisplay
Purpose: Canvas-based audio waveform visualization with progress tracking and interactive seeking
Location: components/WaveformDisplay.tsx
Props & Usage
Props:
| Prop | Type | Description |
|---|---|---|
| audioUrl | string | URL of audio file to visualize |
| progress | number | Current playback progress (0-100) |
| onSeek | (time: number) => void | Callback when user seeks |
| height? | number | Canvas height in pixels (default: 80) |
| barWidth? | number | Waveform bar width (default: 2) |
| barGap? | number | Space between bars (default: 1) |
| primaryColor? | string | Waveform color (default: theme primary) |
| progressColor? | string | Played portion color (default: accent) |
Usage Example:
<WaveformDisplay
audioUrl={song.audioUrl}
progress={currentProgress}
onSeek={handleSeek}
height={100}
primaryColor="#3b82f6"
progressColor="#10b981"
/>
Dependencies: Canvas API, Web Audio API Related: AudioPlayer
Content Cards
SongCard
Purpose: Displays song information with cover image, title, artist, and playback stats
Location: components/SongCard.tsx
Props & Usage
Props:
| Prop | Type | Description |
|---|---|---|
| song | Song | Song object with metadata |
| onClick? | () => void | Click handler for card |
| onPlay? | () => void | Play button handler |
| showArtist? | boolean | Display artist name (default: true) |
| showPlayCount? | boolean | Display play count (default: true) |
| variant? | 'default' | 'compact' | Card layout style |
Usage Example:
<SongCard
song={song}
onClick={() => router.push(`/songs/${song.id}`)}
onPlay={playSong}
showArtist={true}
showPlayCount={true}
variant="default"
/>
Dependencies: Next.js Image, Link Related: TrackList, AudioPlayer
AlbumCard
Purpose: Displays album information with cover art, title, artist, release year, and track count
Location: components/AlbumCard.tsx
Props & Usage
Props:
| Prop | Type | Description |
|---|---|---|
| album | Album | Album object with metadata |
| onClick? | () => void | Click handler for card |
| showArtist? | boolean | Display artist name (default: true) |
| showTrackCount? | boolean | Display track count (default: true) |
Usage Example:
<AlbumCard
album={album}
onClick={() => router.push(`/album/${album.id}`)}
showArtist={true}
showTrackCount={true}
/>
Dependencies: Next.js Image, Link Related: ArtistCard
ArtistCard
Purpose: Displays artist profile with circular avatar, name, and verification badge
Location: components/ArtistCard.tsx
Props & Usage
Props:
| Prop | Type | Description |
|---|---|---|
| artist | Artist | Artist profile object |
| onClick? | () => void | Click handler for card |
| showVerified? | boolean | Display verified badge (default: true) |
| showFollowers? | boolean | Display follower count (default: false) |
| size? | 'sm' | 'md' | 'lg' | Avatar size variant |
Usage Example:
<ArtistCard
artist={artist}
onClick={() => router.push(`/artist/${artist.id}`)}
showVerified={true}
size="md"
/>
Dependencies: Next.js Image, Link Related: AlbumCard, LabelCard
PlaylistCard
Purpose: Displays playlist with cover image, title, song count, and privacy indicator
Location: components/PlaylistCard.tsx
Props & Usage
Props:
| Prop | Type | Description |
|---|---|---|
| playlist | Playlist | Playlist object with metadata |
| onClick? | () => void | Click handler for card |
| showSongCount? | boolean | Display song count (default: true) |
| showPrivacy? | boolean | Display privacy badge (default: true) |
| editable? | boolean | Show edit actions (default: false) |
Usage Example:
<PlaylistCard
playlist={playlist}
onClick={() => router.push(`/playlist/${playlist.id}`)}
showSongCount={true}
showPrivacy={true}
editable={isOwner}
/>
Dependencies: Next.js Image, Link Related: TrackList
LabelCard
Purpose: Displays record label information with logo, name, and artist roster count
Location: components/LabelCard.tsx
Props & Usage
Props:
| Prop | Type | Description |
|---|---|---|
| label | Label | Label profile object |
| onClick? | () => void | Click handler for card |
| showArtistCount? | boolean | Display artist count (default: true) |
Usage Example:
<LabelCard
label={label}
onClick={() => router.push(`/label/${label.id}`)}
showArtistCount={true}
/>
Dependencies: Next.js Image, Link Related: ArtistCard
GenreBadge
Purpose: Small clickable badge displaying music genre with color-coded styling
Location: components/GenreBadge.tsx
Props & Usage
Props:
| Prop | Type | Description |
|---|---|---|
| genre | Genre | Genre object with name and color |
| onClick? | () => void | Click handler for badge |
| size? | 'xs' | 'sm' | 'md' | Badge size variant |
| removable? | boolean | Show remove button (default: false) |
| onRemove? | () => void | Remove button handler |
Usage Example:
<GenreBadge
genre={genre}
onClick={() => filterByGenre(genre.slug)}
size="sm"
/>
Dependencies: None (presentational component) Related: UploadForm
Forms
AuthForm
Purpose: Multi-mode authentication form for login, registration, and password reset flows
Location: components/AuthForm.tsx
Props & Usage
Props:
| Prop | Type | Description |
|---|---|---|
| mode | 'login' | 'register' | 'forgot' | 'reset' | Form mode |
| onSuccess? | () => void | Success callback |
| resetToken? | string | Password reset token (for reset mode) |
Usage Example:
// Login mode
<AuthForm mode="login" onSuccess={handleLoginSuccess} />
// Register mode
<AuthForm mode="register" onSuccess={handleRegisterSuccess} />
// Password reset mode
<AuthForm mode="reset" resetToken={token} onSuccess={handleResetSuccess} />
API Calls:
- Login:
POST /api/auth/login - Register:
POST /api/auth/register - Forgot:
POST /api/auth/forgot-password - Reset:
POST /api/auth/reset-password
Dependencies: React Hook Form, JWT storage Related: UserMenu, Header
UploadForm
Purpose: Multi-field form for uploading songs with audio file, metadata, and genre tagging
Location: components/UploadForm.tsx
Props & Usage
Props:
| Prop | Type | Description |
|---|---|---|
| onSuccess? | (songId: string) => void | Success callback with created song ID |
| onCancel? | () => void | Cancel button handler |
| initialData? | Partial | Pre-fill form data (for editing) |
Usage Example:
<UploadForm
onSuccess={(songId) => router.push(`/songs/${songId}`)}
onCancel={() => router.back()}
/>
Form Fields:
- Audio file upload (required)
- Title (required)
- Album selection (optional)
- Genre tags (multi-select)
- Cover image URL (optional)
- Lyrics (optional)
- Privacy toggle (public/private)
API Calls:
- Upload:
POST /api/songs/upload
Dependencies: File upload handling, GenreBadge Related: GenreBadge
ProfileForm
Purpose: User profile editing form with username, display name, bio, and website fields
Location: components/ProfileForm.tsx
Props & Usage
Props:
| Prop | Type | Description |
|---|---|---|
| user | User | Current user object |
| onSuccess? | () => void | Success callback |
| onCancel? | () => void | Cancel button handler |
Usage Example:
<ProfileForm
user={currentUser}
onSuccess={() => toast.success('Profile updated')}
onCancel={() => router.back()}
/>
Form Fields:
- Username (unique, alphanumeric + underscore)
- Display name (optional)
- Bio (optional, max 500 chars)
- Website URL (optional, validated)
- Avatar URL (optional)
API Calls:
- Update:
PUT /api/users/me
Dependencies: React Hook Form, URL validation Related: UserMenu
CreatePlaylistModal
Purpose: Modal dialog for creating new playlists with title, description, and privacy settings
Location: components/CreatePlaylistModal.tsx
Props & Usage
Props:
| Prop | Type | Description |
|---|---|---|
| isOpen | boolean | Modal open state |
| onClose | () => void | Close modal handler |
| onSuccess? | (playlistId: string) => void | Success callback with playlist ID |
Usage Example:
<CreatePlaylistModal
isOpen={isModalOpen}
onClose={() => setIsModalOpen(false)}
onSuccess={(id) => router.push(`/playlist/${id}`)}
/>
Form Fields:
- Playlist title (required)
- Description (optional)
- Privacy toggle (public/private)
API Calls:
- Create:
POST /api/playlists
Dependencies: Modal backdrop, React Hook Form Related: PlaylistCard
Navigation
Header
Purpose: Fixed top navigation header with logo, navigation links, search, and user menu
Location: components/Header.tsx
Props & Usage
Props:
| Prop | Type | Description |
|---|---|---|
| transparent? | boolean | Transparent background (default: false) |
Usage Example:
<Header transparent={false} />
Features:
- Logo with home link
- Primary navigation links (Discover, Artists, Genres, Labels)
- Search bar integration
- User menu with auth state
- Mobile hamburger menu
- Responsive breakpoints
Dependencies: NavLink, UserMenu, SearchBar Related: NavLink, UserMenu, SearchBar
NavLink
Purpose: Navigation link component with automatic active state highlighting
Location: components/NavLink.tsx
Props & Usage
Props:
| Prop | Type | Description |
|---|---|---|
| href | string | Link destination path |
| children | ReactNode | Link text/content |
| exact? | boolean | Exact path match (default: false) |
| activeClassName? | string | Custom active state class |
Usage Example:
<NavLink href="/discover" exact>
Discover
</NavLink>
<NavLink href="/artist" activeClassName="text-blue-500">
Artists
</NavLink>
Dependencies: Next.js usePathname, Link Related: Header
UserMenu
Purpose: Dropdown menu showing authentication state with profile, upload, playlists, and logout actions
Location: components/UserMenu.tsx
Props & Usage
Props:
| Prop | Type | Description |
|---|---|---|
| user | User | null | Current authenticated user |
Usage Example:
<UserMenu user={currentUser} />
Menu Items (Authenticated):
- Profile link
- Upload song link
- My playlists link
- Settings link (if applicable)
- Logout button
Menu Items (Unauthenticated):
- Login link
- Register link
Dependencies: JWT auth context, Next.js Link Related: Header, AuthForm
SearchBar
Purpose: Search input with real-time suggestions dropdown, debouncing, and loading indicator
Location: components/SearchBar.tsx
Props & Usage
Props:
| Prop | Type | Description |
|---|---|---|
| placeholder? | string | Input placeholder text |
| onSearch? | (query: string) => void | Search submit handler |
| debounceMs? | number | Debounce delay (default: 300ms) |
Usage Example:
<SearchBar
placeholder="Search songs, artists, albums..."
onSearch={(query) => router.push(`/search?q=${query}`)}
debounceMs={300}
/>
Features:
- Real-time search as you type
- Suggestion dropdown with categorized results (Songs, Artists, Albums)
- Loading spinner during API calls
- Keyboard navigation (arrow keys, enter, escape)
- Click outside to close
API Calls:
- Search:
GET /api/search?q={query}
Dependencies: Debounce hook, fetch API Related: Header
Sharing
ShareButton
Purpose: Button that generates shareable links and opens the share modal
Location: components/ShareButton.tsx
Props & Usage
Props:
| Prop | Type | Description |
|---|---|---|
| type | 'song' | 'playlist' | 'album' | Content type to share |
| id | string | Content ID |
| variant? | 'icon' | 'button' | Display style (default: 'button') |
Usage Example:
<ShareButton type="song" id={song.id} variant="button" />
<ShareButton type="playlist" id={playlist.id} variant="icon" />
API Calls:
- Generate link:
POST /api/share/{type}/{id}
Dependencies: ShareModal Related: ShareModal
ShareModal
Purpose: Modal displaying shareable link with copy-to-clipboard and social media share buttons
Location: components/ShareModal.tsx
Props & Usage
Props:
| Prop | Type | Description |
|---|---|---|
| isOpen | boolean | Modal open state |
| onClose | () => void | Close modal handler |
| shareUrl | string | Full shareable URL |
| title | string | Content title for social sharing |
Usage Example:
<ShareModal
isOpen={isShareModalOpen}
onClose={() => setIsShareModalOpen(false)}
shareUrl={shareUrl}
title={song.title}
/>
Features:
- Copy link to clipboard button
- Social share buttons (Twitter, Facebook, WhatsApp)
- QR code generation (optional)
- Share analytics tracking
Dependencies: Clipboard API, social share URLs Related: ShareButton
Display
TrackList
Purpose: Scrollable list of tracks with position numbers, song information, artist details, and duration
Location: components/TrackList.tsx
Props & Usage
Props:
| Prop | Type | Description |
|---|---|---|
| tracks | Song[] | Array of song objects |
| onTrackClick? | (song: Song, index: number) => void | Track selection handler |
| showArtist? | boolean | Display artist name (default: true) |
| showAlbum? | boolean | Display album name (default: false) |
| showDuration? | boolean | Display duration (default: true) |
| numbered? | boolean | Show track numbers (default: true) |
| currentTrackId? | string | ID of currently playing track |
Usage Example:
<TrackList
tracks={albumTracks}
onTrackClick={(song, index) => playTrack(song, index)}
showArtist={true}
showAlbum={false}
numbered={true}
currentTrackId={currentSong?.id}
/>
Features:
- Hover states for interactivity
- Currently playing track highlight
- Click to play functionality
- Responsive column layout
Dependencies: SongCard (data structure) Related: AudioPlayer, AlbumCard, PlaylistCard
Component Dependencies Tree
AudioPlayer
├── PlayerControls
└── WaveformDisplay
Header
├── NavLink
├── UserMenu
│ └── AuthForm
└── SearchBar
ShareButton
└── ShareModal
UploadForm
└── GenreBadge
TrackList
└── (uses Song type)
Standalone Components:
- SongCard
- AlbumCard
- ArtistCard
- PlaylistCard
- LabelCard
- ProfileForm
- CreatePlaylistModal
Styling Conventions
All components use Tailwind CSS for styling with the following patterns:
- Color Palette: Primary (blue), Accent (green), Neutral (gray)
- Spacing: Consistent use of
p-4,m-2,gap-4units - Typography:
text-sm,text-base,text-lg,font-semibold - Responsive: Mobile-first with
sm:,md:,lg:breakpoints - Dark Mode: Not currently implemented (future consideration)
Type Definitions
All components use types defined in /types:
types/song.ts- Song, Album, Artist entitiestypes/user.ts- User, AuthStatetypes/playlist.ts- Playlist, PlaylistSongtypes/api.ts- API request/response types