Frameworks

Framework setup comparison.

Use the same public API across React setups, but place the provider where browser-only audio code is allowed to run.

Comparison

Same hooks, different boundaries.

webaudio-kit stays framework-light. The important choice is where to put AudioProvider so hooks run only in browser-capable React code.

Provider placement

Put AudioProvider around the controls, canvases, and hooks that need the shared master gain and analyser graph. Smaller provider islands are fine when only one screen owns audio playback.

examples/vite-react

Vite React

Wrap the client root or the audio workspace inside AudioProvider in main.tsx.

Call play from click, tap, or key handlers in normal client components.

examples/next-app-router

Next App Router

Keep route files server-rendered when possible, then move AudioProvider and every hook into a client component.

The browser rule is the same, but the play handler must live inside a component marked with use client.

examples/plain-react

Plain React

Create the root with createRoot and wrap either the whole app or the smallest audio control island.

Use a real user gesture before creating or resuming AudioContext.

Vite React root
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import { AudioProvider } from "@webaudio-kit/react";
import { App } from "./App";

createRoot(document.getElementById("root")!).render(
  <StrictMode>
    <AudioProvider>
      <App />
    </AudioProvider>
  </StrictMode>,
);

Next App Router client boundary

Next App Router pages and layouts are server components by default. Keep metadata, static copy, and layout code there, then move AudioProvider, audio hooks, and analyser canvases into a dedicated client component.

app/audio-controls.tsx
"use client";

import { AudioProvider, useTone } from "@webaudio-kit/react";

function ToneButton() {
  const tone = useTone({ frequency: 440, gain: 0.14 });

  return (
    <button onClick={() => void tone.play({ durationMs: 600 })}>
      {tone.isPlaying ? "Restart tone" : "Play tone"}
    </button>
  );
}

export function AudioControls() {
  return (
    <AudioProvider>
      <ToneButton />
    </AudioProvider>
  );
}

Browser autoplay impact

Browser autoplay rules apply equally to Vite, Next, and plain React. Call play() from a user gesture such as a click, tap, or key press so the provider can lazily create and resume AudioContext at the right time.

gesture handler
function PlayButton() {
  const tone = useTone({ frequency: 660, gain: 0.12 });

  return (
    <button onClick={() => void tone.play({ durationMs: 500 })}>
      Play
    </button>
  );
}

Build-checked examples

The standalone examples are intentionally outside the pnpm workspace. Release checks install packed tarballs so the example apps exercise the same package exports developers install from npm.

example verification
pnpm examples:check