WavesurferPlayer

A declarative, memoized React component that renders a wavesurfer.js waveform. The simplest way to drop a player into any component.

Overview

WavesurferPlayer (exported as default from wave-cn.tsx) is a thin wrapper around useWavesurfer. It owns its container <div>, applies WAVESURFER_DEFAULTS, resolves CSS variables automatically, and exposes every wavesurfer.js event as an on-prefixed prop.

Use it when you want a declarative player and don't need to manage the wavesurfer instance yourself.

Usage

import WavesurferPlayer from "@/lib/wave-cn"

<WavesurferPlayer
  url="/coastline.mp3"
  waveColor="var(--primary)"
  height={80}
  onReady={(ws) => console.log("ready", ws)}
  onFinish={() => console.log("finished")}
/>

Getting the instance

Pass onReady to receive the live WaveSurfer instance and store it in a ref for imperative calls:

import { useRef, useCallback } from "react"
import WavesurferPlayer from "@/lib/wave-cn"
import type WaveSurfer from "wavesurfer.js"

export function MyPlayer() {
  const wsRef = useRef<WaveSurfer | null>(null)

  const handleReady = useCallback((ws: WaveSurfer) => {
    wsRef.current = ws
  }, [])

  return (
    <WavesurferPlayer
      url="/coastline.mp3"
      onReady={handleReady}
      onPlay={() => console.log("playing")}
      onPause={() => console.log("paused")}
      onDestroy={() => { wsRef.current = null }}
    />
  )
}

Injecting plugins

Pass a memoized plugins array. The player compares array references — not deep equality — so a new array on every render will recreate the instance on every render.

import { useMemo } from "react"
import WavesurferPlayer from "@/lib/wave-cn"
import ZoomPlugin from "wavesurfer.js/dist/plugins/zoom.esm.js"
import TimelinePlugin from "wavesurfer.js/dist/plugins/timeline.esm.js"

const plugins = useMemo(() => [
  ZoomPlugin.create({ scale: 0.5, maxZoom: 1000 }),
  TimelinePlugin.create(),
], [])

<WavesurferPlayer url="/coastline.mp3" plugins={plugins} />

For plugins that need to be registered after the instance exists (e.g. RecordPlugin), use onReady instead:

<WavesurferPlayer
  url=""
  onReady={(ws) => {
    ws.registerPlugin(RecordPlugin.create({ scrollingWaveform: true }))
  }}
/>

Events

Every wavesurfer.js event is available as an on-prefixed prop. The handler receives the WaveSurfer instance as the first argument, followed by the event's own arguments.

<WavesurferPlayer
  url="/coastline.mp3"
  onReady={(ws) => {}}
  onPlay={(ws) => {}}
  onPause={(ws) => {}}
  onFinish={(ws) => {}}
  onTimeupdate={(ws) => console.log(ws.getCurrentTime())}
  onSeeking={(ws, currentTime) => {}}
  onDestroy={(ws) => {}}
/>

See the full event list at wavesurfer.js docs.

API Reference

Accepts all WaveSurferOptions (except container — the player owns it) plus every on{Event} handler.

PropTypeDefaultDescription
urlstringAudio file URL to load.
waveColorstringvar(--muted-foreground)Accepts CSS vars, hex, hsl, oklch.
progressColorstringvar(--primary)Same formats as waveColor.
heightnumber64Canvas height in px.
barWidthnumber3Bar width in px.
barGapnumber2Gap between bars in px.
barRadiusnumber2Bar border radius.
pluginsGenericPlugin[][]Memoized plugin array.
on{Event}(ws, ...args) => voidAny wavesurfer.js event handler.

On this page