Install the widget

One script tag, anywhere before the closing </body>. No package to install, no build step, no dependency on your framework. Takes about a minute.

Get your project key

Open the Widget page in your dashboard. The snippet there already has your key filled in, so the fastest path is to copy it from there rather than from this page. Keys look like pk_live_a1b2c3… and are safe to put in your page source, see Security & privacy.

Add the script

Pick your stack. Every one of these does the same thing, the only difference is where the tag lives.

index.html
<!-- Anywhere before </body> -->
<script async
  src="https://www.usevoicebox.dev/widget.js"
  data-project="pk_live_your_key"></script>

Site builders and no-code

No developer needed. Each of these is a paste into a settings field.

no plugin required
Appearance → Theme File Editor → footer.php
Paste immediately before </body>:

<script async
  src="https://www.usevoicebox.dev/widget.js"
  data-project="pk_live_your_key"></script>

Using a block theme with no footer.php? Any
"insert headers and footers" plugin works, or add it
under Site Kit / Custom HTML in the footer template.

Check that it worked

Load your site and look for the feedback button in the corner. If it is there, you are done, send yourself a test message and watch it land in the inbox within a few seconds.

If you would rather confirm from the console, this tells you whether the script ran and whether it mounted:

browser console
// Did the script execute?
typeof window.Voicebox === "function"   // → true

// Did it mount?
!!document.querySelector("[data-voicebox]")   // → true

// Force it open, regardless of your trigger settings
Voicebox("open")

Nothing appears?

Almost always one of three things: a Content Security Policy blocking the script, the domain allowlist not including the site you are testing on, or the floating button switched off in the studio. Troubleshooting walks through each in order.

Single-page apps

Nothing to do. The widget mounts once and lives outside your framework's DOM tree, in its own shadow root attached to <body>, so client-side route changes leave it untouched. It does not re-mount, re-fetch its config, or flicker between routes.

The page a submission came from is captured at the moment it is sent, not at page load, so feedback written after three route changes records the URL the person was actually looking at.

Staging and production

Use a separate project per environment rather than one key everywhere. Staging noise stays out of your real themes, and each project keeps its own allowlist so a leaked staging key cannot post to production.

app/layout.tsx
<Script
  src="https://www.usevoicebox.dev/widget.js"
  data-project={process.env.NEXT_PUBLIC_VOICEBOX_KEY}
  strategy="lazyOnload"
/>

Create the second project under Settings → Projects, then set the variable per environment in your host.

Weight and timing

Around 11KB over the wire, no dependencies, and nothing renders until the browser is idle. It never blocks your page, and if our server is unreachable the script fails quietly rather than throwing into your error tracking.