Docs
Docs

Installing the widget

One script tag, the domains it is allowed to run on, and what each report carries.

The script tag

Open your project, go to Widget, and copy the snippet. It is one tag, pasted just before the closing </body>:

html
<script src="https://userwants.app/widget.js"
        data-userwants-key="uw_pk_..."
        data-position="bottom-right"
        defer></script>

There is no package to install and no build step. The file is about 38 KB on disk and roughly 12 KB over the wire, loaded deferred, and everything it renders lives inside a closed shadow root, so it cannot inherit or leak your styles.

The widget key is public by design — it sits in your HTML, where anyone can read it. What stops someone else from using it is the domain allowlist below, not the key.

Allowed domains

Under Widget → Allowed domains, add every domain the widget runs on. Until you add one, the widget only works on localhost — an empty list fails closed rather than open.

Domains are stored normalised to scheme://host[:port], so Acme.com/, https://acme.com and https://acme.com:443 are all the same entry. Two things this means in practice:

  • http:// and https:// are different origins. If you still serve plain HTTP anywhere, add both.
  • acme.com and www.acme.com are different origins. Add both if you serve both.

Wildcards match exactly one level of subdomain:

Entry Matches Does not match
https://*.acme.com app.acme.com, staging.acme.com acme.com, a.b.acme.com

https://*.com is rejected — it would match every .com in existence. The list holds up to 20 entries.

Signed-in visitors

If your site already knows who the visitor is, initialise the widget yourself instead of using the data attributes. Passing an identifier hides the email field:

html
<script src="https://userwants.app/widget.js" defer></script>
<script>
  window.addEventListener('load', function () {
    window.UserWants.init({
      key: 'uw_pk_...',
      identifier: currentUser.id,   // your user id, email or username
      name: currentUser.name,       // optional
      email: currentUser.email      // optional
    });
  });
</script>

An identifier comes from your page, so UserWants cannot verify it — reports arrive marked unverified in the inbox. An email the widget asked for is marked as an email address instead.

In a single-page app, call window.UserWants.identify({ identifier, name, email }) on login and logout rather than re-initialising.

The rest of the API

Call What it does
UserWants.init(options) The page's widget. Calling it again replaces the previous one.
UserWants.create(options) An extra, independent widget that leaves the page's own one alone.
UserWants.identify(identity) Swaps identity without re-initialising.
UserWants.open() / .close() Opens or closes the panel — wire your own "Report a bug" link to it.
UserWants.destroy() Removes it from the page.

Options: key, identifier, name, email, locale, container, config. Language is picked from locale, then your <html lang>, then the browser — English and Spanish ship with the widget.

What a report carries

Every submission carries the title, the description, who sent it, the page URL and the referrer.

Bug reports carry more, as long as Widget → Collect technical details is on:

  • Browser and operating system, viewport and screen size, device pixel ratio, language, time zone, colour scheme and page load time.
  • Up to 10 uncaught JavaScript errors and unhandled promise rejections, each with its stack frame (truncated at 2,000 characters).
  • The last 50 things the person did — clicks, form submits and navigations — as element paths like button#checkout "Pay now".
  • Optionally a screenshot, if the person attaches one.

Never the contents of their forms: a breadcrumb records which field was touched, never what was typed into it. The full field list is on the widget data page, written so you can cite it in your own privacy policy.

Turn Collect technical details off and bug reports arrive with the text only.

Appearance

Accent colour, position, theme and the three visible strings (button label, panel title, success message) are set under Widget → Appearance and load from the server, so you change them without touching your site. The button's text colour is not configurable — it is derived from your accent so the launcher stays readable whatever you pick.

Leave the strings empty to use the widget's own translations. If you write your own, that one string is used in every language.