Skip to content

React

The @arraypress waveform family ships four React wrappers, each a thin, typed component over its vanilla core:

Package Component(s) Wraps
@arraypress/waveform-player-react <WaveformPlayer> @arraypress/waveform-player
@arraypress/waveform-bar-react <WaveformBar>, <WaveformBarTrigger> @arraypress/waveform-bar
@arraypress/waveform-playlist-react <WaveformPlaylist> @arraypress/waveform-playlist
@arraypress/waveform-sounds-react <WaveformSounds> @arraypress/waveform-sounds

Every wrapper follows the same design:

  • Prop types are derived from the core’s own typings (WaveformPlayerOptions; WaveformSoundsOptions for the sound list), not re-declared — so they never drift as the core evolves.
  • The core JS is dynamically import()ed inside useEffect, so the audio + canvas + fetch surface never runs during SSR / RSC.
  • The instance is exposed through forwardRef as a typed imperative handle for loadTrack, seekTo, playlist navigation, and the rest.
  • CSS is never auto-loaded — you import each core’s stylesheet once at your app entry.

react and @arraypress/waveform-player are peer dependencies (React ^18 || ^19; core player ^1.8.0).

Terminal window
npm install @arraypress/waveform-player-react @arraypress/waveform-player

Import the core stylesheet once at your app entry:

// main.tsx / app entry
import '@arraypress/waveform-player/dist/waveform-player.css';
import { WaveformPlayer } from '@arraypress/waveform-player-react';
export default function Track() {
return (
<WaveformPlayer
url="/audio/track.mp3"
title="My Track"
waveformStyle="mirror"
waveformColor={['#fafafa', '#71717a']} // gradient stops
showBPM
onTimeUpdate={(currentTime, duration) => {
/* live progress */
}}
/>
);
}

WaveformPlayerProps extends the core’s WaveformPlayerOptions so every player option is accepted as a typed prop — height, samples, barRadius, colorPreset, accessibleSeek, markers, gradient-array colours, and so on. See Player → Options for the full inherited surface.

The core’s url, style, and the six callbacks are omitted from the inherited set and re-declared with React shapes:

Prop Type Notes
url string Audio file URL. Optional, because the core’s src shorthand alias is also accepted — provide one of url or src.
onLoad (instance) => void Fired after the waveform is drawn. Receives the typed WaveformPlayer instance.
onPlay (instance) => void Playback started (both audio modes).
onPause (instance) => void Playback paused (both modes).
onEnd (instance) => void Track ended (external mode synthesizes it at progress ≥ 1).
onTimeUpdate (currentTime, duration, instance) => void Progress tick. Same argument order in both modes.
onError (error, instance) => void Load / decode / audio error.
id string Forwarded to the container <div>.
className string Appended to the always-present base class wfp-host.
style React.CSSProperties Inline style on the container — e.g. min-height to reserve space before draw.

Grab the instance with a ref typed as WaveformPlayerHandle:

import { useRef } from 'react';
import {
WaveformPlayer,
type WaveformPlayerHandle,
} from '@arraypress/waveform-player-react';
function Controlled() {
const ref = useRef<WaveformPlayerHandle>(null);
return (
<>
<WaveformPlayer ref={ref} url="/audio/track.mp3" />
<button onClick={() => ref.current?.togglePlay()}>Play / Pause</button>
<button onClick={() => ref.current?.seekTo(60)}>Jump to 1:00</button>
<button
onClick={() =>
ref.current?.loadTrack('/audio/next.mp3', 'Next Track', 'Artist')
}
>
Load next
</button>
</>
);
}
Method Signature Notes
play () => Promise<void> | undefined Returns the native play() promise in self mode; undefined in external mode.
pause () => void
togglePlay () => void
seekTo (seconds: number) => void Self mode only.
seekToPercent (percent: number) => void Fraction 0..1. Self mode only.
setVolume (volume: number) => void 0..1. Self mode only.
setPlaybackRate (rate: number) => void Clamped 0.25..4. Self mode only.
setPlayingState (playing: boolean) => void External mode — push play/pause visual state.
setProgress (currentTime, duration) => void External mode — push the progress overlay from your own clock.
loadTrack (url, title?, artist?, options?) => Promise<void> Swap track at runtime; auto-plays unless options.autoplay === false.
instance readonly WaveformPlayer Escape hatch — full core API (load, setWaveformData, refreshTheme, container, statics, …).

Calls before the async import resolves are safe no-ops.


Bar — <WaveformBar> + <WaveformBarTrigger>

Section titled “Bar — <WaveformBar> + <WaveformBarTrigger>”

The bar is a singleton: render <WaveformBar> once in your root layout, then scatter <WaveformBarTrigger> elements anywhere to play or queue tracks.

Peer deps: @arraypress/waveform-bar (^1.3.1), @arraypress/waveform-player (^1.7.2), React ^18 || ^19.

Terminal window
npm install @arraypress/waveform-bar-react @arraypress/waveform-bar @arraypress/waveform-player
import { WaveformBar } from '@arraypress/waveform-bar-react';
export default function RootLayout({ children }) {
return (
<>
{children}
<WaveformBar
config={{
persist: true,
continuous: true,
showQueue: true,
actions: {
favorite: { endpoint: '/api/favorites' },
cart: { endpoint: '/api/cart' },
},
}}
/>
</>
);
}

On mount the component dynamically imports the core and calls window.WaveformBar.init(config). When config changes it re-runs init() (idempotent — the library destroys and recreates internally); on unmount it calls destroy() so StrictMode double-mounts and route changes don’t leak listeners.

<WaveformBar> prop Type Default Notes
config WaveformBarConfig undefined Passed verbatim to init(). See table below.
persist boolean true Relocate the .waveform-bar element under the host <div> so it survives route changes.
hostId string 'waveform-bar-host' DOM id of the persist host.
className string — Appended to the base class wb-host.
style React.CSSProperties — Inline style on the host.

Bar-react hand-declares this type (the bar core ships no .d.ts), so this is the source of truth for the typed config surface. Every field is optional.

Group Fields
Behaviour persist, autoResume, continuous, repeat ('off' | 'all' | 'one')
UI toggles showQueue, showPrevNext, showRepeat, showVolume, showMute, showTime, showTrackLink, showMeta, maxMeta (number)
Layout mode ('waveform' | 'classic'), wide, position ('bottom' | 'top'), collapsible, showShuffle, shuffle
Theming defaultArtwork (string | null), theme ('dark' | 'light' | null)
Waveform waveform (boolean), waveformStyle, waveformHeight, barWidth, barSpacing, waveformColor, progressColor, markerColor
Sharing / errors share, shareParam, errorText
Volume / storage volume, storageKey
Server actions actions ({ favorite?, cart? }, each { endpoint, method?, headers? })

<WaveformBarTrigger> emits the data-wb-* attribute contract the bar scans for. It is polymorphic — a <button> by default — and forwards every standard DOM prop (onClick, data-testid, role, ref, …) to the rendered element.

import { WaveformBarTrigger } from '@arraypress/waveform-bar-react';
function ProductCard({ track }) {
return (
<article>
<h3>{track.title}</h3>
{/* Default: a <button> with auto play/pause icons */}
<WaveformBarTrigger
url={track.url}
id={track.id}
title={track.title}
artist={track.artist}
artwork={track.cover}
/>
{/* Append to the queue with custom content */}
<WaveformBarTrigger mode="queue" url={track.url} title={track.title}>
+ Queue
</WaveformBarTrigger>
{/* Wrap a whole card as the trigger */}
<WaveformBarTrigger as="div" url={track.url} noDefaultIcons>
<div className="card-body">{/* … */}</div>
</WaveformBarTrigger>
</article>
);
}

Track data props (WaveformBarTrackData):

Prop Type Notes
url string Required — play target + identity.
id string Defaults to url.
title, artist, album, artwork, link string Display metadata.
duration, bpm string | number
musicalKey string → data-wb-key.
meta string[] Extra metadata chips.
waveform number[] | string Pre-computed peaks or .json URL.
markers WaveformBarMarker[] DJ-mode cue markers ({ time, label, title?, artist?, artwork?, bpm?, key?, color? }).
favorited, inCart boolean Initial action state.

Trigger-specific props:

Prop Type Default Notes
mode 'play' | 'queue' 'play' Immediate play vs. append to queue.
as 'button' | 'a' | 'div' | 'span' 'button' Rendered tag.
href string — Used when as="a".
aria-label string auto Auto-generated from title when absent.
className string — Appended to the base class wb-icon-swap.
noDefaultIcons boolean false Suppress the injected play/pause SVGs.
children ReactNode — Custom content; overrides the default icons.

A declarative tracks array with optional chapters, an embedded player, and imperative track / chapter navigation.

Peer deps: @arraypress/waveform-playlist (^1.3.0), @arraypress/waveform-player (^1.8.0), React ^18 || ^19.

Terminal window
npm install @arraypress/waveform-playlist-react @arraypress/waveform-playlist @arraypress/waveform-player

Import both stylesheets, and import the player core for its global side effect — the playlist constructs new window.WaveformPlayer(...) for the active track:

import '@arraypress/waveform-player/dist/waveform-player.css';
import '@arraypress/waveform-playlist/dist/waveform-playlist.css';
import '@arraypress/waveform-player'; // registers window.WaveformPlayer
import { WaveformPlaylist } from '@arraypress/waveform-playlist-react';
export default function Album() {
return (
<WaveformPlaylist
continuous
waveformStyle="bars"
tracks={[
{ url: '/audio/a.mp3', title: 'Track A', artist: 'Artist' },
{
url: '/audio/b.mp3',
title: 'Track B',
chapters: [
{ time: 0, label: 'Intro' },
{ time: '1:30', label: 'Verse' },
{ time: 180, label: 'Chorus', color: '#22d3ee' },
],
},
]}
/>
);
}

Each track renders into the [data-track] / [data-chapter] markup the playlist constructor parses on mount. The host carries the base class wfp-host.

WaveformPlaylistTrackInput[]:

Field Type Notes
url string Required → data-url.
title string Falls back to the filename if omitted.
artist string Artist row.
artwork string Artwork URL.
album string Media Session metadata.
duration string Display duration, e.g. '3:45'.
markers WaveformPlaylistMarker[] JSON-encoded into data-markers.
chapters WaveformPlaylistChapterInput[] { time: number | string, label, color? } — time accepts seconds or 'M:SS'.

WaveformPlaylistProps combines three groups:

  1. Playlist options picked from WaveformPlaylistOptions: continuous, expandChapters, showDuration, showChapterMarkers, chapterMarkerColor, showPlayState.
  2. Pass-through player options — the full WaveformPlayerOptions visualization / colour / behaviour surface, minus per-track content (url, src, title, artist, artwork, album, markers, waveform), the style alias, the player’s own layout, and the lifecycle callbacks.
  3. React extras — the required tracks, plus id, className, style.
Prop Type Default Notes
tracks WaveformPlaylistTrackInput[] — Required. An empty array renders an empty playlist (the core skips init).
layout 'list' | 'minimal' 'list' Full track list vs. compact button switcher. Overrides the player’s own layout.
id string — Forwarded to the host <div>.
className string — Appended to wfp-host.
style React.CSSProperties — Inline style on the host.

Common pass-through player props include waveformStyle, height, colorPreset, waveformColor, progressColor, showBPM, accessibleSeek, and autoplay — see Player → Options for the complete list.

import { useRef } from 'react';
import {
WaveformPlaylist,
type WaveformPlaylistHandle,
} from '@arraypress/waveform-playlist-react';
function NavExample({ tracks }) {
const ref = useRef<WaveformPlaylistHandle>(null);
return (
<>
<WaveformPlaylist ref={ref} tracks={tracks} continuous />
<button onClick={() => ref.current?.previousTrack()}>Prev</button>
<button onClick={() => ref.current?.nextTrack()}>Next</button>
<button onClick={() => ref.current?.selectTrack(0)}>First</button>
</>
);
}
Method Signature Notes
selectTrack (index: number) => void Select + load a track by index.
seekToChapter (trackIndex, time) => void Loads the target track first if needed, then seeks.
nextTrack () => void
previousTrack () => void
getPlayer () => unknown | null The embedded WaveformPlayer instance (full player API).
getCurrentTrackIndex () => number
getTracks () => WaveformPlaylistTrack[] Parsed tracks (with resolved element / index).
instance readonly WaveformPlaylist | null Escape hatch for anything else.

Calls before the async import resolves are no-ops.


A searchable, filterable list of sound previews — a sample pack, a preset bank — with a mini waveform per row and one shared audio engine. Unlike the components above it renders its markup in React: with sounds, the whole list is in the first render (server HTML included) and the runtime adopts it on the client instead of rebuilding it. See WaveformSounds for what the list does.

Peer deps: @arraypress/waveform-sounds (^0.1.0), @arraypress/waveform-player (^1.24.5), React ^18 || ^19.

Terminal window
npm install @arraypress/waveform-sounds-react @arraypress/waveform-sounds @arraypress/waveform-player

Import both stylesheets, and import the player for its global — the list plays through window.WaveformPlayer, and the wrapper doesn’t import it for you (or pass the class as playerClass). With the player in place, the list builds its engine as soon as it’s ready; no audio loads until something plays.

import '@arraypress/waveform-player'; // registers window.WaveformPlayer, the engine
import '@arraypress/waveform-player/styles.css';
import '@arraypress/waveform-sounds/styles.css';
import { WaveformSounds } from '@arraypress/waveform-sounds-react';
export default function Pack() {
return (
<WaveformSounds
sounds={[
{ url: '/s/bass-01.mp3', title: 'Bass Loop 01', type: 'Bass', bpm: 128, key: 'Fm' },
{ url: '/s/drum-01.mp3', title: 'Drum Loop 01', type: 'Drums', bpm: 140 },
]}
onPlay={(sound) => console.log(sound.title)}
/>
);
}

Or from a waveform-gen --manifest file, fetched and built in the browser: <WaveformSounds manifest="/sounds.json" />. To server-render a manifest, read it at build or request time and pass its sounds array.

Every WaveformSoundsOptions key is a prop, typed from the core’s own index.d.ts — sounds / manifest, player, search, filters, sorts, showCount, menuSearch, idPrefix, urlState, loopToggle, maxTypeChips, pageSize, columns, waveformStyle, waveformColor, progressColor, barWidth, barGap, loop, autoAdvance, arrowAudition, playerOptions, playerClass, strings — plus the callbacks onReady / onPlay / onPause / onEnd / onFilter / onError, and id / className / style for the host. See Options for each one.

  • Server markup. With sounds, the host’s inner HTML is the core’s own renderSounds() output. With only manifest, the host is empty and the list is fetched and built client-side. The host carries wfp-host waveform-sounds waveform-sounds--<player> from the first render, so it’s styled before hydration, and never data-waveform-sounds (the global auto-init must not mount it twice).
  • Dropdown ids are unique per list and stable across hydration: the idPrefix prop, else the id prop, else one from React’s useId().
  • urlState is runtime-only: the server renders the unfiltered list and the runtime applies the address on load.
  • A changed option re-creates the list, over freshly rendered markup. Arrays and objects (sounds, filters, sorts, columns, strings, playerOptions) compare by value, so inline literals are fine. A changed callback never re-creates it — the latest one is always called, and the ref is live by the time onReady and the first onFilter fire.

Theme it through style — set --ws-surface (your page background) so the server-rendered first paint is right:

<WaveformSounds sounds={sounds} style={{ '--ws-surface': '#0a0a0a' } as React.CSSProperties} />
import { useRef } from 'react';
import { WaveformSounds, type WaveformSoundsHandle } from '@arraypress/waveform-sounds-react';
function Pack({ sounds }) {
const ref = useRef<WaveformSoundsHandle>(null);
return (
<>
<WaveformSounds ref={ref} sounds={sounds} />
<button onClick={() => ref.current?.setFilter({ type: 'Bass' })}>Bass</button>
<button onClick={() => ref.current?.setSort('bpm')}>By BPM</button>
</>
);
}
Method Signature Notes
play (target?, opts?: { at?: number }) => void By index, id or Sound; no target resumes.
pause () => void
toggle (target?) => void
next / previous () => void The next / previous visible sound.
setFilter (patch: Partial<SoundsFilter>) => void Merged into the current filter.
clearFilters () => void The sort stays.
setSort (by: SoundsSort) => void
setLoop (on: boolean) => void Live — changing the loop prop re-creates the list instead.
showMore () => void
instance readonly WaveformSounds | null The core instance (visible, current, sounds, engine, ready, …).

Calls before the instance mounts are no-ops. The core’s bubbling waveformsounds:* events fire from the host as well.


All four components are SSR / RSC safe by construction: the host markup renders on the server, and the browser-only core is dynamically import()ed inside useEffect so it only evaluates client-side. <WaveformSounds> goes further and renders the whole list on the server (from the core’s DOM-free /render entry) when given sounds. The interactive UI hydrates in once the library loads. Reserve layout space with the style prop (e.g. style={{ minHeight: 96 }}) to avoid a content shift before the waveform draws.

Astro

The declarative data-* wrappers — Astro.