Components / Audio
Music Playlist
Free Audio React component — MusicPlaylistCard. MIT licensed, copy-paste ready for Next.js and Tailwind CSS.
Playlist card with cover, header stats, and a track list where each row toggles play independently. One component, full playlist UI.
Preview

Playlist
Chill Vibes
24 songs · 1h 32m
Setup
How to use
Free for personal and commercial use. No UI attribution required. Include the MIT copyright notice when copying component source into your project. Read the MIT license.
- 1
Run in your terminal:
$npm install clsx tailwind-merge lucide-react - 2
Copy
lib/cn.tsbelow. Skip if you already havecn(). - 3
Copy the code below and create
components/audio/music-playlist-card.tsxin your project. - 4
Import and render:
Exampleimport { MusicPlaylistCard } from "@/components/audio/music-playlist-card";
lib/cn.ts
import { type ClassValue, clsx } from "clsx";import { twMerge } from "tailwind-merge";export function cn(...inputs: ClassValue[]) {return twMerge(clsx(inputs));}
components/audio/music-playlist-card.tsx
"use client";import { forwardRef, useState, type ComponentPropsWithoutRef } from "react";import Image from "next/image";import { cn } from "@/lib/cn";import { Pause, Play } from "lucide-react";export type PlaylistTrack = {title: string;artist: string;duration: string;};export type MusicPlaylistCardProps = Readonly<{coverImage?: string;playlistType?: string;title?: string;songCount?: string;totalDuration?: string;tracks?: PlaylistTrack[];} & ComponentPropsWithoutRef<"div">>;// Production-ready Music Playlist component — styled with Tailwind CSS.export const MusicPlaylistCard = forwardRef<HTMLDivElement,MusicPlaylistCardProps>(({className,coverImage = "/wallpaper-3.png",playlistType = "Playlist",title = "Chill Vibes",songCount = "24 songs",totalDuration = "1h 32m",tracks = [{title: "Blinding Lights",artist: "The Weekend",duration: "3:20",},{title: "Save Your Tears",artist: "The Weekend",duration: "3:35",},{title: "Starboy",artist: "The Weekend",duration: "3:50",},],...props},ref,) => {const [playingIndex, setPlayingIndex] = useState<number | null>(null);const toggleTrack = (index: number) => {setPlayingIndex((prev) => (prev === index ? null : index));};return (<divref={ref}data-slot="music-playlist-card"className={cn("w-72 overflow-hidden rounded-2xl border border-neutral-100 bg-white font-sans shadow-lg",className,)}{...props}><div data-slot="music-playlist-card-header" className="flex gap-3 p-4"><divdata-slot="music-playlist-card-cover"className="relative h-16 w-16 shrink-0 overflow-hidden rounded-xl shadow-sm"><Imagesrc={coverImage}alt={title}fillsizes="64px"className="object-cover"/></div><div data-slot="music-playlist-card-info"><p className="font-mono text-[10px] tracking-wider text-neutral-400 uppercase">{playlistType}</p><h3 className="mt-0.5 text-sm font-semibold text-neutral-900">{title}</h3><p className="mt-0.5 text-[11px] text-neutral-500">{songCount} · {totalDuration}</p></div></div><divdata-slot="music-playlist-card-tracks"className="divide-y divide-neutral-50">{tracks.map((track, index) => {const isPlaying = playingIndex === index;return (<buttonkey={`${track.title}-${track.artist}`}type="button"onClick={() => toggleTrack(index)}data-slot="music-playlist-card-track"className={cn("group flex w-full cursor-pointer items-center gap-3 px-4 py-2.5 text-left transition-colors hover:bg-neutral-50",isPlaying && "bg-emerald-50/80",)}><spanclassName={cn("w-4 font-mono text-[10px] text-neutral-400",isPlaying ? "hidden" : "group-hover:hidden",)}>{index + 1}</span><spanclassName={cn("w-4 text-[10px] text-emerald-600",isPlaying ? "block" : "hidden group-hover:block",)}>{isPlaying ? (<Pause size={12} fill="green" />) : (<Play size={12} fill="green" />)}</span><div className="min-w-0 flex-1"><pclassName={cn("truncate text-xs font-medium",isPlaying ? "text-emerald-700" : "text-neutral-800",)}>{track.title}</p><p className="text-[10px] text-neutral-400">{track.artist}</p></div><span className="font-mono text-[10px] text-neutral-400">{track.duration}</span></button>);})}</div></div>);},);MusicPlaylistCard.displayName = "MusicPlaylistCard";