Everything you need to build an audio player is in a single file: the engine, the UI controls, the queue, tracks, and playback speed. No separate installs, no cross-file wiring. Just one component you copy, paste, and own.
Installation
pnpm dlx shadcn@latest add @audio/playerPhilosophy
Composable by default. AudioPlayer is just a container. Every control (seek bar, volume, queue, playback speed) is an independent component you drop in where you need it. No black-box layout, no hidden markup.
State lives in the store, not in components. All playback state (current track, progress, queue, shuffle, repeat) is held in a Zustand store. Components read and write that store directly. No prop drilling, no context threading.
The engine is separate from the UI. AudioProvider wires up the HTML audio element to the store: event listeners, retries, preloading, state restoration. UI components just read state and call store actions.
Setup
Pass tracks directly to AudioPlayer for a self-contained widget:
import { AudioPlayer, AudioPlayerControlBar, AudioPlayerPlay } from "@/components/audio/player";
<AudioPlayer tracks={myTracks}>
<AudioPlayerControlBar>
<AudioPlayerPlay />
</AudioPlayerControlBar>
</AudioPlayer>For shared state across multiple players or app-wide persistence, mount AudioProvider once at the root:
// app/layout.tsx
import { AudioProvider } from "@/components/audio/player";
export default function RootLayout({ children }) {
return (
<html>
<body>
<AudioProvider tracks={initialTracks}>{children}</AudioProvider>
</body>
</html>
);
}tracks vs AudioProvider: Use tracks on AudioPlayer for self-contained widgets. Use AudioProvider at the root when multiple components share the same playback session.
Player
Compact
With queue
Variants
default
ghost
Sizes
sm
default
AudioPlayer
Container for all player controls.
| Prop | Type | Default | Description |
|---|---|---|---|
tracks | Track[] | - | When provided, wraps children with AudioProvider automatically. |
variant | "default" | "ghost" | "default" | Visual style. ghost is transparent with a hover background. |
size | "sm" | "default" | "default" | Controls padding and border radius. |
className | string | - | Additional CSS classes. |
Inherits all other props from HTMLDivElement. The underlying CVA definition is exported as audioPlayerVariants for extension.
AudioProvider
Connects the HTML audio element to the Zustand store. Handles event listeners, retries, preloading, and state restoration.
| Prop | Type | Default | Description |
|---|---|---|---|
tracks | Track[] | [] | Initial tracks to populate the queue. |
children | ReactNode | - | Components that share this audio session. |
AudioPlayerButton
Shared button primitive used by all player controls. Wraps a Button with an optional tooltip.
| Prop | Type | Default | Description |
|---|---|---|---|
tooltipLabel | string | - | When provided, wraps the button in a tooltip. |
Inherits all other props from Button.
AudioPlayerControlBar
Groups controls in a single row or stacked column layout.
| Prop | Type | Default | Description |
|---|---|---|---|
variant | "compact" | "stacked" | "compact" | Controls the layout. |
AudioPlayerControlGroup
Flexible row wrapper for arranging control clusters. Use className to adjust alignment.
AudioPlayerPlay
Play/pause button. Shows a spinner during loading/buffering. Spacebar shortcut is enabled automatically.
Inherits all props from AudioPlayerButton.
AudioPlayerSkipBack / AudioPlayerSkipForward
Navigate between tracks.
Inherits all props from AudioPlayerButton.
AudioPlayerRewind / AudioPlayerFastForward
Seek backward or forward by 10 seconds. Disabled for live streams.
Inherits all props from AudioPlayerButton.
AudioPlayerSeekBar
Seek timeline showing playback and buffered progress. Locked to 100% for live streams.
Inherits all props from Transport (except value, onSeek, bufferedValue).
AudioPlayerTimeDisplay
Displays current time or remaining time. Shows a live indicator for streams.
| Prop | Type | Default | Description |
|---|---|---|---|
remaining | boolean | false | Show remaining time instead of elapsed time. |
AudioPlayerVolume
Dropdown with a horizontal fader to control volume and mute.
Inherits all props from Fader (except value, onValueChange, min, max, orientation, size).
Tracks
AudioTrackList renders a list of tracks. Omit tracks to read from the global queue; pass tracks for controlled mode.
Single track
Default
Grid
Sortable
Sortable Grid
AudioTrack
Renders a single track row.
| Prop | Type | Default | Description |
|---|---|---|---|
trackId | string | number | - | Look up a track from the queue by id (store mode). |
track | Track | - | Render a provided track object (controlled mode). |
index | number | - | Display index (one-based when shown). |
onClick | () => void | - | Called when the row is clicked. |
onRemove | (trackId: string) => void | - | Called when the remove action is triggered. |
media | "cover" | "drag-handle" | "drag-handle-with-cover" | "index" | "cover" | Left-side media variant. |
actions | "none" | "play-pause" | "remove" | "play-pause-with-remove" | "play-pause" | Right-side action variant. |
Mode constraint: Use either trackId or track, not both. trackId requires an AudioProvider in the tree.
AudioTrackList
| Prop | Type | Default | Description |
|---|---|---|---|
tracks | Track[] | - | Controlled list. Omit to read from the global queue. |
onTrackSelect | (index: number, track?: Track) => void | - | Called when a track is selected. |
onTrackRemove | (trackId: string) => void | - | Called when a track is removed. Enables remove actions automatically. |
mode | "static" | "sortable" | "static" | Enable drag-and-drop reordering. |
media | "cover" | "index" | "cover" | Media style. In sortable mode a drag handle is added automatically. |
actions | "none" | "play-pause" | "remove" | "play-pause-with-remove" | Auto | Action variant per row. Auto: "play-pause-with-remove" when onTrackRemove is set. |
variant | "default" | "grid" | "default" | Stacked list or responsive grid. |
filterQuery | string | - | Text filter (matches title or artist). |
filterFn | (track: Track) => boolean | - | Custom filter. Overrides filterQuery when both are set. |
emptyLabel | string | "No tracks found" | Empty state label. |
emptyDescription | string | "Try adding some tracks" | Empty state description. |
Sortable constraint: Drag-and-drop reordering only updates the store when the list is unfiltered and in store mode.
Queue
All queue controls work together or independently.
Simple
All controls
Shuffle and repeat
Preferences
AudioQueue
A dialog with the full queue: search, selection, remove, and drag-and-drop reordering.
| Prop | Type | Default | Description |
|---|---|---|---|
onTrackSelect | (index: number) => void | - | Called when a track is selected from the queue. |
searchPlaceholder | string | "Search for a track..." | Search input placeholder. |
emptyLabel | string | "No tracks found" | Empty state label. |
emptyDescription | string | "Try searching for a different track" | Empty state description. |
Search and reorder: Drag-and-drop is disabled while a search filter is active.
AudioQueueShuffle
Toggle that enables/disables shuffle. Persisted to localStorage.
Inherits all props from Toggle (except onPressedChange).
AudioQueueRepeatMode
Toggle that cycles through repeat modes: none → all → one. Persisted to localStorage.
Inherits all props from Toggle.
AudioQueuePreferences
Dropdown combining repeat mode and insert mode (first, last, after current) in one compact menu.
Inherits all props from AudioPlayerButton.
Playback Speed
AudioPlaybackSpeed
Dropdown to change playback speed. Disabled automatically for live streams.
| Prop | Type | Default | Description |
|---|---|---|---|
speeds | readonly { value: number; label: string }[] | PLAYBACK_SPEEDS | Speed options. Default: 0.5×, 0.75×, 1×, 1.25×, 1.5×, 2×. |
size | ButtonSize | - | "icon" hides the gauge icon, shows only the label. |
variant | ButtonVariant | "outline" | Button variant. |
Notes
Live streams: Seeking, rewind, fast-forward, and playback speed are disabled automatically. The seek bar locks to 100%.
Spacebar shortcut: AudioPlayerPlay registers a global keydown listener for Space when the document body is focused.
Changelog
2026-04-17 data-slot coverage and AudioPlayer variants
- Added:
AudioPlayernow acceptsvariant(default|ghost|outline) andsize(sm|default|lg) props via CVA - Added:
audioPlayerVariantsexported for extension - Added:
data-slotattributes on all components for CSS targeting (audio-player,audio-player-button,audio-seek-bar,audio-track,audio-track-list,audio-queue-trigger) - Added:
data-current="true"onAudioTrackwhen it is the active track - Added:
data-variantonAudioPlayerandAudioPlayerControlBar
2026-04-15 Unified into single file
- Changed:
provider.tsx,queue.tsx,track.tsx,playback-speed.tsxmerged intoplayer.tsx - Changed: Provider logic extracted into
useAudioProviderhook - Added:
AudioPlayernow accepts atracksprop, self-provisions the audio engine - Removed:
AudioQueueButton,AudioPlaybackSpeedButton, consolidated intoAudioPlayerButton
2026-04-05 Player API alignment and transport migration
- Changed:
AudioPlayerSeekBarnow usesTransportinstead ofSlider - Changed:
AudioPlayerVolumenow uses horizontalFader - Improved:
AudioPlayerButtonappliesaria-labelfromtooltipLabelby default
2025-12-24 useAudio integration
- Changed: Provider now uses
useAudio()hook for the audio singleton - Improved: Event listeners managed via
AbortController
Last updated 4/17/2026
On This Page