React audio player with compound-slot layout (waveform, playlist, ARIA) — recent update added playback speed in 3 surfaces

typescript dev.to

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();
Enter fullscreen mode Exit fullscreen mode

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 an audioInitialState.playbackRate field for "always start at 1.5×" cases.
  • Dropdown customization for <Volume /> and <SpeedSelector />triggerType: "click" | "hover", four-side placement. Resolution: compound prop > UIContext > component default.
  • Track-swap silence fix on bar-progress players (next/track-end and PREV paths both bump audioResetKey now).
  • Three small breaking changes worth a glance before pinning: a constant rename (defaultInterfacePlacementMaxLengthDEFAULT_INTERFACE_GRID_BOUND), the row1-10 grid cell now reserved for the default speed control, and the Volume click-mode role change.

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.

Source: dev.to

arrow_back Back to Tutorials