Skip to main content

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:

Feedback text
The free-form description the reporter types. This is the core of every widget report.
Annotations
Marks the reporter draws on the page (boxes, highlights) to point at the problem.
Screenshot (optional)
A capture of the current view, only if the reporter chooses to attach one.
Page context
The current URL, viewport size, and user agent — lightweight metadata that helps you reproduce the issue.

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.

Privacy by design

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

PropTypeRequiredDescription
projectKeystringYesYour public widget key (bp_pub_…). Identifies the project and environment the report belongs to.
publicKeystringYes*Alias for projectKey. Pass one or the other, not both.
environmentstringRecommendedThe environment label for this build, e.g. "staging" or "production". Helps you separate reports by where they came from.
apiBaseUrlstringNoAPI 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 }NoIdentifies the reporter so reports are attributed. All fields are optional.
Either 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',
})
No-build sites

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.

CallbackFires whenArgument
onSubmittedA report is accepted by the APIresult — includes the report's dashboard URL
onErrorSubmission failserror — the failure that occurred
onSubmitPayloadJust before submitpayload — mutate it to enrich the report

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
}}
/>
💡
Use 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.

  1. 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).

    <BugPortWidget
    projectKey="bp_pub_local_xxxxxxxx"
    environment="local"
    apiBaseUrl="http://localhost:8000/v1"
    />
  2. Production

    Omit apiBaseUrl to use the hosted default, and use your production key.

    <BugPortWidget
    projectKey="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.

Do
  • Create a separate widget key per environment (local, staging, production)
  • Add every origin the widget runs on, including http://localhost:3000 for local dev
  • Rotate or revoke a key from the dashboard if it leaks or you stop using an origin
Don't
  • 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

SymptomCauseFix
403 / origin rejectedThe page's origin is not on the widget key's allowed-origins listAdd the origin (including the protocol and port) to the key in the dashboard
404 on submitapiBaseUrl is wrong or missing the version suffixMake sure apiBaseUrl ends in /v1 (e.g. http://localhost:8000/v1)
Nothing rendersRunning on the server, or React is too oldEnsure React 18+ and that the widget mounts client-side (see the Next.js note above)
Verify the request path

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.

Next steps