1
0 Comments

react-modern-audio-player — React audio library with compound-slot layout (recent update added playback speed)

Maintaining an open-source React audio library called react-modern-audio-player and wanted to surface a few open design questions to anyone who's wrestled with media UI in React.

The frame I went with: every built-in control (Progress, Volume, PlayList, PlayButton, SpeedSelector, etc.) is a compound slot. So you mix the preset layout with custom placement instead of swapping the whole component or rebuilding the layout from scratch.

Default full view

Same controls, moved to different grid cells:

Custom grid placement

State is also 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" (tooltip semantics aren't right for an interactive panel). Same shape as video.js and Vidstack.

Optional waveform mode via wavesurfer.js:

Waveform progress mode

Latest 2.3.1 added: playback speed in three surfaces (compound slot, hook API, initial-state field), dropdown customization for Volume and SpeedSelector (triggerType, placement), and a track-swap silence fix on bar-progress players.

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

Open questions for anyone who's built media UIs

  1. Compound-slot pattern vs. preset-only — has anyone tried this pattern (preset layout + per-slot override) for audio/video controls? What broke when you scaled the grid past a few cells? I'm curious whether the resolution order (compound prop > UIContext > component default) holds up under deeper composition.

  2. Discrete-rate selector role — for playback-rate UIs, would you reach for role="menu" + menuitemradio (current choice) or role="combobox" with typeahead? I went with menu because the rate set is small and fixed, but I'm not sure if combobox would feel more natural for keyboard users on the rare 0.5×–2× span.

  3. formatRate callback shape — went through several revisions: (rate: number) => string (current), render prop, hardcoded × suffix, intl.NumberFormat-style options. Which would you reach for in your own code, and why?

Genuinely open to being told I picked the wrong tradeoff on any of these. Drop a take below if you've shipped something adjacent.

posted to Icon for group Developers
Developers
on May 1, 2026
Trending on Indie Hackers
How are you handling memory and context across AI tools? User Avatar 112 comments Do you actually own what you build? User Avatar 66 comments Code is Cheap, but Scaling AI MVPs is Hard. Let’s Fix Yours. User Avatar 34 comments I Think MCP Will Punish Thin API Wrappers User Avatar 27 comments What AI Is Actually Changing in IT Certification Prep User Avatar 19 comments Cloud vs Cybersecurity Certifications | 2026 Path Makes More Sense User Avatar 18 comments