useWavesurfer
The primary React hook for wavesurfer.js. Wraps instance creation, state tracking, CSS variable resolution, and shared defaults into a single call.
Overview
useWavesurfer is the lower-level alternative to WavesurferPlayer. Use it when you need full control over the container element, want to co-locate the waveform inside a complex layout, or need to read reactive state (isPlaying, currentTime, etc.) directly in your component.
Usage
import { useRef } from "react"
import { useWavesurfer } from "@/lib/wave-cn"
export function MyPlayer() {
const containerRef = useRef<HTMLDivElement | null>(null)
const { wavesurfer, isReady, isPlaying, currentTime } = useWavesurfer({
container: containerRef,
url: "/coastline.mp3",
})
return (
<div>
<div ref={containerRef} />
<button onClick={() => wavesurfer?.playPause()} disabled={!isReady}>
{isPlaying ? "Pause" : "Play"}
</button>
<span>{currentTime.toFixed(2)}s</span>
</div>
)
}Overriding defaults
Any option overrides WAVESURFER_DEFAULTS. Passing undefined gracefully falls back to the default — no need to repeat shared values in every component.
// All WAVESURFER_DEFAULTS apply
useWavesurfer({ container, url })
// Override only what you need
useWavesurfer({ container, url, height: 120, barWidth: 5 })With plugins
Always memoize the plugins array — the hook uses array reference equality to decide whether to recreate the instance.
import { useMemo } from "react"
import { useWavesurfer } from "@/lib/wave-cn"
import ZoomPlugin from "wavesurfer.js/dist/plugins/zoom.esm.js"
const plugins = useMemo(
() => [ZoomPlugin.create({ scale: 0.5, maxZoom: 1000 })],
[],
)
const { wavesurfer, isReady } = useWavesurfer({ container, url, plugins })For plugins that require the instance to already exist, register them in a useEffect:
import { useEffect } from "react"
import { useWavesurfer } from "@/lib/wave-cn"
import RecordPlugin from "wavesurfer.js/dist/plugins/record.esm.js"
const { wavesurfer } = useWavesurfer({ container, url: "" })
useEffect(() => {
if (!wavesurfer) return
wavesurfer.registerPlugin(RecordPlugin.create({ scrollingWaveform: true }))
}, [wavesurfer])What recreates the instance
Only url, media, plugins, peaks, duration, sampleRate, backend and mediaControls recreate the instance. Every other option (height, barWidth, colors, audioRate, …) is applied in place through wavesurfer.setOptions() and never re-decodes the audio.
plugins and peaks are compared by reference, so keep them in useMemo.
Imperative calls
Playback state (speed, volume, position) is still best driven imperatively:
wavesurfer?.setPlaybackRate(speed, true)
wavesurfer?.setVolume(0.5)
wavesurfer?.seekTo(0.5) // 0–1 normalizedIf you need play/pause/seek/volume state with matching actions, use useWavePlayer together with WavesurferPlayer instead.
API Reference
Parameters
| Option | Type | Default | Description |
|---|---|---|---|
container | RefObject<HTMLDivElement> | — | Required. Ref to the container element. |
url | string | — | Wave file URL. |
waveColor | string | var(--muted-foreground) | Accepts CSS vars, hex, hsl, oklch. |
progressColor | string | var(--primary) | Same formats as waveColor. |
height | number | 64 | Canvas height in px. |
barWidth | number | 3 | Bar width in px. |
barGap | number | 2 | Gap between bars in px. |
barRadius | number | 2 | Bar border radius. |
minPxPerSec | number | 1 | Minimum pixels per second (zoom level). |
plugins | GenericPlugin[] | [] | Memoized plugin templates (cloned on each create). |
peaks | Array<Float32Array | number[]> | — | Precomputed peaks, memoized. |
duration | number | — | Duration in seconds, used with peaks. |
...rest | WaveSurferOptions | — | Any other wavesurfer.js option. |
Returns
| Property | Type | Description |
|---|---|---|
wavesurfer | WaveSurfer | null | The live instance. null until mounted. |
isReady | boolean | true after the ready event fires. |
isPlaying | boolean | true while wave is playing. |
currentTime | number | Current playback position in seconds. |