react-modern-audio-player is a React audio library I've been maintaining. The angle that might be worth a look if you're building anything beyond a single play button: every built-in control is exposed as a compound slot, so you can mix the preset layout with custom placement instead of swapping the whole component.
Slots you can drop in (subset):
-
<AudioPlayer.Progress />— bar or wavesurfer-backed waveform -
<AudioPlayer.Volume />— trigger + slider, hover or click mode, four-side placement -
<AudioPlayer.PlayList />— sortable, drag-and-drop -
<AudioPlayer.SpeedSelector />— discrete-rate menu -
<AudioPlayer.CustomComponent />— user-defined slot
Move any of them to a different cell of the layout grid:
The same state is reachable through an imperative hook, so the UI doesn't have to live inside the player frame:
const { playbackRate, setPlaybackRate, currentTime, isPlaying } = useAudioPlayer();
A11y follows WAI-ARIA APG out of the box — the rate menu uses role="menu" + role="menuitemradio" with aria-checked, and the click-mode Volume uses role="dialog" (not tooltip — tooltip semantics aren't right for an interactive panel). Same patterns video.js and Vidstack use.
Optional waveform mode via wavesurfer.js:
What landed in 2.3.1
-
Playback speed, exposed three orthogonal ways: the
<AudioPlayer.SpeedSelector />slot,useAudioPlayer().setPlaybackRate(rate), and anaudioInitialState.playbackRatefield for "always start at 1.5×" cases. -
Dropdown customization for
<Volume />and<SpeedSelector />—triggerType: "click" | "hover", four-sideplacement. Resolution: compound prop > UIContext > component default. -
Track-swap silence fix on bar-progress players (next/track-end and PREV paths both bump
audioResetKeynow). - Three small breaking changes worth a glance before pinning: a constant rename (
defaultInterfacePlacementMaxLength→DEFAULT_INTERFACE_GRID_BOUND), therow1-10grid cell now reserved for the default speed control, and the Volume click-moderolechange.
Stack: TypeScript-first, Next.js App Router compatible (Server Components), wavesurfer.js for the optional waveform mode, no UI-framework dependency.
Repo, README, full changelog:
https://github.com/slash9494/react-modern-audio-player
Feedback on the SpeedSelector ergonomics specifically would be useful — the formatRate callback shape went through a couple of revisions and I'm not sure it landed in the right place.