v2.0.0
β¨ New Features
-
colorSchemeprop: forces the player to render in"light"or"dark"mode regardless of the OSprefers-color-schemesetting. When omitted, the player follows the OS preference (existing behavior). The provider applies adata-themeattribute on the root container, and the WaveSurfer instance is automatically re-initialized when the prop changes so waveform colors stay in sync.// β Before β native CSS property, did not actually toggle the theme <AudioPlayer rootContainerProps={{ style: { colorScheme: "dark" } }} /> // β After β top-level prop, drives `[data-theme]` + wavesurfer re-init <AudioPlayer colorScheme="dark" />
-
useAudioPlayer()public hook: exposes a stable API to control the player externally- Returns
play,pause,togglePlay,next,prev,seek,setVolume,setTrack - Returns state:
isPlaying,volume,currentTime,duration,repeatType,muted,currentTrack,currentIndex,playList - Must be called inside
AudioPlayerProvider(orAudioPlayerwhich wraps it)
- Returns
-
Domain-specific sub-hooks: Fine-grained alternatives to
useAudioPlayerthat subscribe to only the context slice they need, minimizing re-renders:useAudioPlayerPlayback()β playback state and controlsuseAudioPlayerTrack()β playlist and track navigationuseAudioPlayerVolume()β volume and mute controlsuseAudioPlayerTime()β current time, duration, and seekuseAudioPlayerElement()β rawaudioElandwaveformInstrefs (advanced)
-
useAudioPlayer().toggleMute(): Facade now exposestoggleMutefrom the volume sub-hook. -
Conditional progress rendering:
WaveformProgressandBarProgressare now conditionally rendered based onactiveUI.progressinstead of rendering both and toggling via CSS. Only the active component is mounted.
π Bug Fixes
- Waveform crash on theme toggle after track change: toggling the theme after switching tracks in waveform mode no longer throws
TypeError: Cannot read properties of null (reading 'muted'). Worked around a wavesurfer.js 6.6.4 listener leak (MediaElement._setupMediaListeners()overwritesmediaListenersbefore callingremoveEventListener, orphaning prior closures on everywaveform.load()) by detaching stale backend listeners before each load, and replaced destroy+recreate with in-placesetWaveColor/setProgressColorupdates on theme change. - WaveSurfer memory leak on unmount:
waveformInst.destroy()was never called due to a stale closure in the cleanup effect. Fixed by tracking the instance viauseRef. - Theme color switching: system dark/light theme changes now correctly update progress bar, volume slider, shadow, and waveform colors
- Progress bar handle position: handle no longer drifts when player placement changes (replaced JS-based width tracking with CSS container query units)
- Waveform progress rendering: waveform blue progress layer now redraws correctly on container resize and placement changes
- Waveform theme re-initialization: waveform canvas re-creates with correct colors when system color scheme changes
useDidUpdateEffectStrictMode fix: boolean ref was flipped inside deps effect causing premature execution on React 18 StrictMode's setupββcleanupββsetupβ cycle. Fixed by separating mount tracking into its own empty-deps effect.- Volume reset on source load: browser resets volume to 1.0 when loading a new audio source. Volume is now re-applied after
loadedmetadata. audioInitialStatenot applied correctly (Fixes #9)- Generic type error
InterfaceGridTemplateArea(Fixes #22)
π SSR / Next.js Compatibility
'use client'directive: Added to the library entry point so Next.js App Router consumers can import<AudioPlayer>directly from Server Components. ForuseAudioPlayer()hooks andAudioPlayer.CustomComponentcompound pattern, consumers must use'use client'in their own component (standard RSC behavior, same as Radix/Chakra).useIsomorphicLayoutEffect: AlluseLayoutEffectcalls replaced with an isomorphic wrapper that falls back touseEffectduring SSR.- SSR-safe
window/documentaccess:isBrowserguard utility added; applied touseVariableColoranduseGridTemplate. useLayoutEffectβuseEffectdowngrade: 6 call sites that did not require layout timing were downgraded touseEffect.- Fixes #20 β Next.js 14
ReferenceError: self is not defined - Fixes #12 β
Cannot find name 'WaveSurfer'when building Next.js app - Fixes #7, #13 β CSS syntax errors / failed to compile global CSS
βΏ Accessibility
- Dropdown
useId+ ARIA attributes:Dropdownnow generates a uniquedropdownIdviauseId().DropdownTriggergainsaria-expandedandaria-controlsattributes. - Fixes #10 β Clicking control buttons no longer triggers form submit
π¦ Bundle Size Optimization
| Change | Before | After | Saving |
|---|---|---|---|
@react-spectrum/* (4 pkgs) |
bundled (~20β30 kB) | removed β native HTML + CSS | ~20β30 kB |
react-icons (3 icon sets) |
bundled (~15β25 kB) | removed β 12 inlined SVGs | ~15β25 kB |
wavesurfer.js |
always bundled (~192 kB raw) | lazy chunk, loaded on demand | ~192 kB from main |
styled-components |
bundled | removed β vanilla CSS | ~15 kB |
sideEffects |
not set | ["*.css"] |
better tree-shaking |
| Main bundle (gzip) | ~65 kB+ | ~16 kB | ~75% reduction |
π₯ Breaking Changes
-
reactandreact-domminimum version is now>=18.0.0: Projects using React 16/17 must stay on v1.x. -
SpectrumProviderrenamed toAudioPlayerContainer: import name changed- Before:
import { AudioPlayer, SpectrumProvider } from "react-modern-audio-player" - After:
import { AudioPlayer, AudioPlayerContainer } from "react-modern-audio-player" - (Interim name
AudioPlayerRootProviderwas also removed;AudioPlayerContaineris the final name.)
- Before:
-
SpectrumProviderPropsrenamed toAudioPlayerContainerProps: type import name changed (interimAudioPlayerRootProviderPropsalso removed) -
AudioPlayerContainerProps.rootContainerPropstype changed:ProviderProps(React Spectrum) βHTMLAttributes<HTMLDivElement> -
Provider directory reorganized: Old import paths under
components/Provider/are removed. Providers now live undercomponents/AudioPlayer/Provider/(AudioPlayerStateProvider) andcomponents/AudioPlayer/Container/(AudioPlayerContainer). Package entry consumers unaffected; deep-import users must update paths. -
AudioPlayerProviderrenamed toAudioPlayerStateProvider: internal state provider name changed. -
useAudioPlayerrelocated:hooks/useAudioPlayerβapi/useAudioPlayer. Package entry consumers unaffected; deep-import users must update paths. -
useAudioPlayerfacade decomposition: Now a convenience facade composing domain-specific sub-hooks. Signature compatible. For fine-grained re-render control, use individual sub-hooks:useAudioPlayerPlayback()β{ isPlaying, repeatType, play, pause, togglePlay }useAudioPlayerTrack()β{ currentPlayId, currentIndex, playList, currentTrack, setTrack, next, prev }useAudioPlayerVolume()β{ volume, muted, setVolume, toggleMute }useAudioPlayerTime()β{ currentTime, duration, seek }useAudioPlayerElement()β{ audioEl, waveformInst }(advanced)
-
PrevNnextBtnremoved: Split intoPrevBtnandNextBtn. Propvisiblerenamed toisVisible. CSS classrmap-prev-next-btnreplaced byrmap-prev-btnandrmap-next-btn. -
styled-componentsremoved: The library no longer usesstyled-components. It is removed from bothdependenciesandpeerDependencies. All styles are now vanilla CSS. -
audioPlayerStateContextremoved: Split into 4 domain-specific contexts (~70% re-render reduction).import { usePlaybackContext, // isPlaying, repeatType, volume, muted useTrackContext, // playList, curIdx, curPlayId useUIContext, // activeUI, placements useResourceContext, // elementRefs, customIcons, coverImgsCss } from "react-modern-audio-player";
-
CSS class names prefixed with
rmap-: e.g.,btn-wrapperβrmap-ctrl-btn-wrapper. CSS custom properties (--rm-audio-player-*) are unchanged. -
rm-audio-player-providerclass renamed tormap-player-provider -
ElementRefsremoved:trackCurTimeEl,trackDurationEl,progressBarEl,progressValueEl,progressHandleElβ all now driven by React state. -
AudioData.nametype narrowed:string | ReactNodeβstring(usecustomTrackInfofor ReactNode) -
AudioData.writertype narrowed:string | ReactNodeβstring(usecustomTrackInfofor ReactNode)
New Contributors
- @saschabuehrle made their first contribution in #46
Full Changelog: v1.3.0...v2.0.0