No track selected
0:00--:--
"use client"
import { PauseIcon, PlayIcon } from "lucide-react"
import { cn } from "@/lib/utils"
import {
AudioPlayerButton,
AudioPlayerDuration,
AudioPlayerProgress,
AudioPlayerProvider,
AudioPlayerTime,
exampleTracks,
useAudioPlayer,
} from "@/components/ui/audio-player"
import { Button } from "@/components/ui/button"
import { Card } from "@/components/ui/card"
import { ScrollArea } from "@/components/ui/scroll-area"
interface Track {
id: string
name: string
url: string
}
export function AudioPlayer() {
return (
<AudioPlayerProvider<Track>>
<AudioPlayerDemo />
</AudioPlayerProvider>
)
}
const AudioPlayerDemo = () => {
return (
<Card className="w-full overflow-hidden p-0">
<div className="flex flex-col lg:h-[180px] lg:flex-row">
<div className="bg-muted/50 flex flex-col overflow-hidden lg:h-full lg:w-64">
<ScrollArea className="h-48 w-full lg:h-full">
<div className="space-y-1 p-3">
{exampleTracks.map((song, index) => (
<SongListItem
key={song.id}
song={song}
trackNumber={index + 1}
/>
))}
</div>
</ScrollArea>
</div>
<Player />
</div>
</Card>
)
}
const Player = () => {
const player = useAudioPlayer<Track>()
return (
<div className="flex flex-1 items-center p-4 sm:p-6">
<div className="mx-auto w-full max-w-2xl">
<div className="mb-4">
<h3 className="text-base font-semibold sm:text-lg">
{player.activeItem?.data?.name ?? "No track selected"}
</h3>
</div>
<div className="flex items-center gap-3 sm:gap-4">
<AudioPlayerButton
variant="outline"
size="default"
className="h-12 w-12 shrink-0 sm:h-10 sm:w-10"
disabled={!player.activeItem}
/>
<div className="flex flex-1 items-center gap-2 sm:gap-3">
<AudioPlayerTime className="text-xs tabular-nums" />
<AudioPlayerProgress className="flex-1" />
<AudioPlayerDuration className="text-xs tabular-nums" />
</div>
</div>
</div>
</div>
)
}
const SongListItem = ({
song,
trackNumber,
}: {
song: Track
trackNumber: number
}) => {
const player = useAudioPlayer<Track>()
const isActive = player.isItemActive(song.id)
const isCurrentlyPlaying = isActive && player.isPlaying
return (
<div className="group/song relative">
<Button
variant={isActive ? "secondary" : "ghost"}
size="sm"
className={cn(
"h-10 w-full justify-start px-3 font-normal sm:h-9 sm:px-2",
isActive && "bg-secondary"
)}
onClick={() => {
if (isCurrentlyPlaying) {
player.pause()
} else {
player.play({
id: song.id,
src: song.url,
data: song,
})
}
}}
>
<div className="flex w-full items-center gap-3">
<div className="flex w-5 shrink-0 items-center justify-center">
{isCurrentlyPlaying ? (
<PauseIcon className="h-4 w-4 sm:h-3.5 sm:w-3.5" />
) : (
<>
<span className="text-muted-foreground/60 text-sm tabular-nums group-hover/song:invisible">
{trackNumber}
</span>
<PlayIcon className="invisible absolute h-4 w-4 group-hover/song:visible sm:h-3.5 sm:w-3.5" />
</>
)}
</div>
<span className="truncate text-left text-sm">{song.name}</span>
</div>
</Button>
</div>
)
}
Installation
pnpm dlx @elevenlabs/agents-cli@latest components add audio-player
Usage
import {
AudioPlayerButton,
AudioPlayerDuration,
AudioPlayerProgress,
AudioPlayerProvider,
AudioPlayerTime,
useAudioPlayer,
useAudioPlayerTime,
} from "@/components/ui/audio-player"
Basic Player
<AudioPlayerProvider>
<div className="flex items-center gap-4">
<AudioPlayerButton />
<AudioPlayerProgress className="flex-1" />
<AudioPlayerTime />
<span>/</span>
<AudioPlayerDuration />
</div>
</AudioPlayerProvider>
Playing a Specific Track
const track = {
id: "track-1",
src: "/audio/song.mp3",
data: { title: "My Song", artist: "Artist Name" }
}
<AudioPlayerProvider>
<AudioPlayerButton item={track} />
<AudioPlayerProgress />
</AudioPlayerProvider>
Multiple Tracks
const tracks = [
{ id: "1", src: "/audio/track1.mp3", data: { title: "Track 1" } },
{ id: "2", src: "/audio/track2.mp3", data: { title: "Track 2" } },
{ id: "3", src: "/audio/track3.mp3", data: { title: "Track 3" } },
]
<AudioPlayerProvider>
<div className="space-y-4">
{tracks.map((track) => (
<div key={track.id} className="flex items-center gap-4">
<AudioPlayerButton item={track} />
<span className="text-sm">{track.data.title}</span>
</div>
))}
<AudioPlayerProgress className="w-full" />
<div className="flex gap-2 text-sm">
<AudioPlayerTime />
<span>/</span>
<AudioPlayerDuration />
</div>
</div>
</AudioPlayerProvider>
API Reference
AudioPlayerProvider
The provider component that manages audio state and playback. Must wrap all audio player components.
<AudioPlayerProvider>{children}</AudioPlayerProvider>
AudioPlayerButton
A play/pause button that controls playback. Shows a loading spinner when buffering.
Props
Prop | Type | Description |
---|---|---|
item | AudioPlayerItem<TData> | Optional. The audio item to play. If not provided, controls the current track |
...props | ButtonProps | All standard Button component props |
AudioPlayerItem Type
interface AudioPlayerItem<TData = unknown> {
id: string | number
src: string
data?: TData
}
AudioPlayerProgress
A slider that shows playback progress and allows seeking. Pauses during seeking and resumes after.
Props
Prop | Type | Description |
---|---|---|
...props | SliderProps | All Radix UI Slider props except min , max , and value |
AudioPlayerTime
Displays the current playback time in formatted time (e.g., "1:30").
Props
Prop | Type | Description |
---|---|---|
className | string | Optional CSS classes |
...props | HTMLSpanElement | All standard span element props |
AudioPlayerDuration
Displays the total duration of the current track or "--:--" when unavailable.
Props
Prop | Type | Description |
---|---|---|
className | string | Optional CSS classes |
...props | HTMLSpanElement | All standard span element props |
useAudioPlayer Hook
Access the audio player context to control playback programmatically.
const {
ref, // RefObject<HTMLAudioElement>
activeItem, // Current playing item
duration, // Track duration in seconds
error, // MediaError if any
isPlaying, // Playing state
isBuffering, // Buffering state
isItemActive, // Check if an item is active
setActiveItem, // Set the active item
play, // Play audio
pause, // Pause audio
seek, // Seek to time
} = useAudioPlayer<TData>()
Example Usage
function PlaylistController() {
const { play, pause, isPlaying, activeItem } = useAudioPlayer()
const handlePlayNext = () => {
const nextTrack = getNextTrack(activeItem?.id)
if (nextTrack) {
play(nextTrack)
}
}
return <button onClick={handlePlayNext}>Next Track</button>
}
useAudioPlayerTime Hook
Get the current playback time (updates every frame using requestAnimationFrame).
const time = useAudioPlayerTime() // Current time in seconds
Example Usage
function CustomTimeDisplay() {
const time = useAudioPlayerTime()
const { duration } = useAudioPlayer()
const percentage = duration ? (time / duration) * 100 : 0
return <div>Progress: {percentage.toFixed(1)}%</div>
}
Advanced Examples
Custom Controls
function CustomAudioPlayer() {
const { play, pause, isPlaying, seek, duration } = useAudioPlayer()
return (
<div className="space-y-4">
<button onClick={() => (isPlaying ? pause() : play())}>
{isPlaying ? "Pause" : "Play"}
</button>
<button onClick={() => seek(0)}>Restart</button>
<button onClick={() => duration && seek(duration * 0.5)}>
Jump to 50%
</button>
</div>
)
}
Error Handling
function AudioPlayerWithError() {
const { error, activeItem } = useAudioPlayer()
if (error) {
return (
<div className="text-red-500">
Failed to load: {activeItem?.src}
<br />
Error: {error.message}
</div>
)
}
return <AudioPlayerButton />
}
Notes
- The audio player uses the HTML5 audio element under the hood
- Progress updates are synchronized using
requestAnimationFrame
for smooth UI updates - The player handles buffering states and network errors automatically
- Space bar triggers play/pause when the progress slider is focused
- The component includes TypeScript support with generic data types
- Audio state is managed globally within the provider context
On This Page
InstallationUsageBasic PlayerPlaying a Specific TrackMultiple TracksAPI ReferenceAudioPlayerProviderAudioPlayerButtonPropsAudioPlayerItem TypeAudioPlayerProgressPropsAudioPlayerTimePropsAudioPlayerDurationPropsuseAudioPlayer HookExample UsageuseAudioPlayerTime HookExample UsageAdvanced ExamplesCustom ControlsError HandlingNotesDeploy and Scale Agents with ElevenLabs
ElevenLabs delivers the infrastructure and developer experience you need to ship reliable audio & agent applications at scale.
Talk to an expert