Install · Django
Bug reports and feedback from your Django site, in one script tag.
Where it goes
In your base template, usually templates/base.html, just before the closing body tag.
Setup
Adding the widget to Django
- 01
Copy your widget key
From the Widget tab of your project in UserWants. The key starts with uw_pk_ and is public by design: it is rendered into every response you send, and the domain allowlist rather than secrecy is what limits where it works.
- 02
Add the tag to your base template
Every template that extends base.html inherits the widget from a single edit, including the admin-facing pages if they share the base. Read the key from settings rather than hard-coding it.
html{# templates/base.html #} {% block content %}{% endblock %} <script src="https://userwants.app/widget.js" data-userwants-key="{{ USERWANTS_KEY }}" defer ></script> </body> - 03
Expose the key through a context processor
Add a tiny context processor so the template does not reach into settings itself. This is also what lets staging and production point at different UserWants projects from the same template.
python# context_processors.py from django.conf import settings def userwants(request): return {'USERWANTS_KEY': settings.USERWANTS_KEY} - 04
Identify the signed-in user and allow your domain
Django already knows who is authenticated, so passing the id costs three lines and means the reporter is never asked for an email. Then add your production hostname under Allowed domains in the Widget tab, since the list fails closed.
html{% if user.is_authenticated %} <script> window.UserWants?.identify({ identifier: "{{ user.pk }}", name: "{{ user.get_full_name|escapejs }}", email: "{{ user.email|escapejs }}" }); </script> {% endif %}
Worth knowing
What bites on Django
django-csp needs two directives
If you use django-csp, add https://userwants.app to CSP_SCRIPT_SRC and CSP_CONNECT_SRC. With only the script directive the button appears and submitting fails silently, which is the hardest version of this bug to diagnose.
Always escape user values
The escapejs filter above is not optional. A display name containing a quote will otherwise break the script tag and take the widget down with it.
It does not see your Python tracebacks
The widget collects JavaScript errors from the browser. A 500 from a Django view produces an error page, and the report will carry the URL, the screenshot and the description — pair it with Sentry or your logs for the traceback.
FAQ
Questions
- Is there a pip package or Django app to install?
- No. Nothing runs on your server, so there is no package, no middleware, no settings block beyond one key and no migrations. The only Django code is the optional context processor that passes the key to your template.
- Does it work with Django templates and HTMX together?
- Yes. HTMX swaps fragments rather than reloading the document, so the widget attaches once and stays. Each swap that changes the URL is recorded as a step, which shows in the report as the path the person took.
The full reference — every field the widget collects, the domain allowlist rules and the JavaScript API — is in the widget docs.
Try it on your Django site.
Paste the tag, add your domain, and the next report arrives with the screenshot and the JavaScript error already attached.