Documentation menu

Framework Adapters

The core runtime is framework agnostic. React has an official adapter; with other frameworks, create the emitter in a client-side lifecycle hook and destroy it on cleanup.

React

@puejs/react binds an emitter to the component lifecycle. It supports React 18 and 19, Strict Mode and the Next.js App Router.

npm install @puejs/core @puejs/react
TSX
"use client"; import { usePue } from "@puejs/react"; export function Feed({ children }: { children: React.ReactNode }): React.JSX.Element {  const { ref, state, start, stop } = usePue<HTMLDivElement>({ preset: "crisp" });   return (    <>      <button aria-pressed={state === "running"} onClick={() => (state === "idle" ? start() : stop())} type="button">        Acoustic feedback      </button>      <div ref={ref} style={{ overflowY: "auto", height: "100vh" }}>        {children}      </div>    </>  );}

The emitter is created on mount, updated when options change and destroyed on unmount. Without ref, the hook observes window; passing false instead of options disables it. Audio only starts through start() — or usePue(options, { autoStart: true }), which still waits for a user gesture.

Sharing defaults

Wrap your application in PueProvider to share default options and a single AudioContext between every usePue call:

TSX
import { PueProvider } from "@puejs/react"; export default function RootLayout({ children }: { children: React.ReactNode }): React.JSX.Element {  return (    <html lang="en">      <body>        <PueProvider defaults={{ preset: "organic", volume: 0.6 }}>{children}</PueProvider>      </body>    </html>  );}

Vue

Vue
<script setup lang="ts">import { createEmitter, type Emitter } from "@puejs/core";import { onBeforeUnmount, onMounted, ref } from "vue"; const feed = ref<HTMLElement | null>(null);let emitter: Emitter | null = null;const start = (): void => void emitter?.start(); onMounted(() => {  emitter = createEmitter(feed.value!, { preset: "organic" });  window.addEventListener("pointerdown", start, { once: true });});onBeforeUnmount(() => {  window.removeEventListener("pointerdown", start);  emitter?.destroy();});</script> <template>  <section ref="feed" class="feed"><slot /></section></template>

Roadmap

Official adapters remove this boilerplate:

ModuleStatus
@puejs/reactAvailable
@puejs/vuePlanned
@puejs/sveltePlanned
@puejs/solidPlanned

Follow progress on GitHub.