"use client", and every sound is safe to import during server rendering: it does nothing until it runs in a browser.
Set up
Runinit from the root of your Next.js app:
init suggests a sounds directory based on your project layout, and the @/* alias that create-next-app adds to tsconfig.json covers it:
Play a sound from a client component
Sounds play in response to user interaction, so call them from a client component:app/components/save-button.tsx
app/page.tsx
You can import a sound in a server component, but you can’t pass it to a client component as a prop: functions aren’t serializable. Import sounds inside the client components that play them.
Add a mute setting
Put<SoundProvider> in a client providers file and wrap your root layout with it. Every useSound below it follows its enabled and volume props, and also stays silent when the user has prefers-reduced-motion: reduce set.
app/providers.tsx
app/layout.tsx
localStorage in an effect, as above, rather than during render. Reading it during render gives a different result on the server and the client and causes a hydration mismatch.
See Use with React for a toggle component that drives this setting.
Sounds outside components
Server actions, route handlers and middleware run on the server, where there is no audio. Play the sound on the client when the result comes back:app/components/subscribe-form.tsx
Pages Router
The same code works in the Pages Router. Wrap your app inpages/_app.tsx instead of a root layout. You don’t need "use client" there.
pages/_app.tsx
Next steps
Pick sounds for each interaction
The 43 semantic sounds and when to use each one.
UI sound best practices
When to play sounds, and how to keep them accessible.