NPM Widget
@bugport.dev/widget is a small React component that lets your users report bugs from inside your own app — no browser extension required. The reporter writes feedback, optionally annotates the page or attaches a screenshot, and the widget submits a structured report straight to your BugPort project. It is the right choice when you want feedback capture available to every user of a web app (customers, testers, teammates) rather than only to people who have installed the browser extension.
v0.1.0 The npm package ships a React component and an imperative mount API. A CDN/IIFE build for no-build sites is Planned and is not shipped yet.
What the widget captures
The widget is intentionally minimal and reporter-driven. It only collects what the person filing the report actually triggers:
It does not silently record console logs, network traffic, or session replay. If you need that level of fidelity, use the browser extension. The widget is the lighter-weight, in-product path.
Because the widget captures only feedback, annotations, an optional screenshot, and page context, there is far less surface area for accidentally storing secrets. Screenshots can still contain whatever is visible on screen, so treat them like any other user-submitted media. See Security & Privacy for recommended practices.
Install
npm install @bugport.dev/widget
The package requires React 18+ and renders on the client only.
Basic React usage
Render BugPortWidget once, high in your tree (for example in your root layout), and pass it a public widget key. You create that key per project per environment in the dashboard — see Create a project for how to generate bp_pub_… keys and configure allowed origins.
import { BugPortWidget } from '@bugport.dev/widget'
export function App() {
return (
<>
{/* your app */}
<BugPortWidget
projectKey="bp_pub_xxxxxxxxxxxxxxxx"
environment="production"
/>
</>
)
}
That is enough to get a working feedback launcher. By default the widget talks to the hosted API at https://api.bugport.dev/v1; you only set apiBaseUrl when pointing at a local or self-hosted backend.
Props
| Prop | Type | Required | Description |
|---|---|---|---|
projectKey | string | Yes | Your public widget key (bp_pub_…). Identifies the project and environment the report belongs to. |
publicKey | string | Yes* | Alias for projectKey. Pass one or the other, not both. |
environment | string | Recommended | The environment label for this build, e.g. "staging" or "production". Helps you separate reports by where they came from. |
apiBaseUrl | string | No | API base URL. Defaults to https://api.bugport.dev/v1. Override for local/self-hosted. Must end in /v1. |
user | { id?: string; email?: string; name?: string } | No | Identifies the reporter so reports are attributed. All fields are optional. |
projectKey or its alias publicKey is required — supply exactly one.
Attaching a user makes triage far easier because each report names who hit the bug:
<BugPortWidget
projectKey="bp_pub_xxxxxxxxxxxxxxxx"
environment="production"
user={{ id: 'u_123', email: 'jordan@acme.com', name: 'Jordan Lee' }}
/>
Imperative mounting
If you are not rendering inside a React tree — or you want to mount the widget from a script tag in an existing build — use the imperative API. Both initBugPortWidget and the global window.BugPort.init take the same options as the component props.
import { initBugPortWidget } from '@bugport.dev/widget'
initBugPortWidget({
projectKey: 'bp_pub_xxxxxxxxxxxxxxxx',
environment: 'production',
user: { email: 'jordan@acme.com' },
})
When the package is loaded on the page, it also exposes a global initializer:
window.BugPort.init({
projectKey: 'bp_pub_xxxxxxxxxxxxxxxx',
environment: 'production',
})
The imperative API still requires the npm package to be bundled and on the page. A standalone CDN/IIFE build that you can drop in via a single <script src="…"> tag is Planned and not available yet. For now, install from npm and bundle it with your app.
Callbacks
Three optional callbacks let you hook into the submit lifecycle. Each receives a single argument.
| Callback | Fires when | Argument |
|---|---|---|
onSubmitted | A report is accepted by the API | result — includes the report's dashboard URL |
onError | Submission fails | error — the failure that occurred |
onSubmitPayload | Just before submit | payload — mutate it to enrich the report |
onSubmitted(result) — confirm and link
Use this to give the reporter feedback that their report landed, and to surface the dashboard link (for example to your own support team).
<BugPortWidget
projectKey="bp_pub_xxxxxxxxxxxxxxxx"
environment="production"
onSubmitted={(result) => {
toast.success('Thanks! Your report was sent.')
console.log('View in dashboard:', result.dashboardUrl)
}}
/>
onError(error) — handle failures gracefully
Surface a friendly message and log the failure to your own monitoring so a misconfigured key or origin does not silently swallow reports.
<BugPortWidget
projectKey="bp_pub_xxxxxxxxxxxxxxxx"
environment="production"
onError={(error) => {
toast.error('We could not send your report. Please try again.')
myMonitoring.captureException(error)
}}
/>
onSubmitPayload(payload) — enrich before submit
Mutate the outgoing payload to attach app-specific context — a build hash, the active feature flag, the current route name. This keeps your reports richer without changing what the reporter has to do.
<BugPortWidget
projectKey="bp_pub_xxxxxxxxxxxxxxxx"
environment="production"
onSubmitPayload={(payload) => {
payload.environment = `prod@${__BUILD_SHA__}`
return payload
}}
/>
onSubmitPayload to stamp every report with the data your team always asks for first — build version, tenant, or feature flag — so triage starts with answers instead of questions.Local development vs production
The only difference between environments is the apiBaseUrl and which widget key you use.
- Local / self-hosted
Point the widget at your running backend and use a key whose allowed origins include your dev origin (for example
http://localhost:3000).<BugPortWidgetprojectKey="bp_pub_local_xxxxxxxx"environment="local"apiBaseUrl="http://localhost:8000/v1"/> - Production
Omit
apiBaseUrlto use the hosted default, and use your production key.<BugPortWidgetprojectKey="bp_pub_prod_xxxxxxxx"environment="production"/>The default base URL is
https://api.bugport.dev/v1.
Allowed origins
Public widget keys are safe to ship in client code because they are origin-restricted. Each bp_pub_… key carries an allowed-origins list, and the API rejects submissions from any origin not on that list. Before a key works on a given site, add that site's origin to the key's allowed origins in the dashboard.
- Create a separate widget key per environment (local, staging, production)
- Add every origin the widget runs on, including
http://localhost:3000for local dev - Rotate or revoke a key from the dashboard if it leaks or you stop using an origin
- Reuse a production key on staging or local — keep environments isolated
- Forget to add new preview/branch origins, or submissions will be rejected with a 403
React and client-side rendering notes
The widget mounts in the browser and touches DOM/browser APIs, so it must run on the client.
- React 18+ is required.
- Client-side only. In a server-rendered framework, ensure the component is not rendered during SSR.
- Next.js. Render the widget inside a Client Component — add the
'use client'directive at the top of the file that uses it. If you hit hydration or "window is not defined" issues, load it with a dynamic import that disables server rendering. The general pattern looks like this:
'use client'
import dynamic from 'next/dynamic'
const BugPortWidget = dynamic(
() => import('@bugport.dev/widget').then((m) => m.BugPortWidget),
{ ssr: false },
)
export function Feedback() {
return (
<BugPortWidget
projectKey="bp_pub_xxxxxxxxxxxxxxxx"
environment="production"
/>
)
}
The exact dynamic-import approach varies by framework version; the key requirement is simply that the widget initializes in the browser, not on the server.
Common integration errors
| Symptom | Cause | Fix |
|---|---|---|
| 403 / origin rejected | The page's origin is not on the widget key's allowed-origins list | Add the origin (including the protocol and port) to the key in the dashboard |
| 404 on submit | apiBaseUrl is wrong or missing the version suffix | Make sure apiBaseUrl ends in /v1 (e.g. http://localhost:8000/v1) |
| Nothing renders | Running on the server, or React is too old | Ensure React 18+ and that the widget mounts client-side (see the Next.js note above) |
A quick way to diagnose a 404 is to open your browser's network tab and confirm submissions hit a URL ending in /widget/bugs under a base that ends in /v1.