Introduction
Bugline is a browser SDK that turns in-app user feedback and unhandled client-side errors into developer-ready bug reports. By dropping a single lightweight widget into your web application, you let users, QA testers, and clients report issues directly from the page—without leaving your product.
Instead of vague email descriptions, every report Bugline sends automatically carries the current page path, browser name and version, operating system, screen resolution, language, and user agent. Users can attach a screenshot, pin the report to a specific element, or draw a highlight box around the problem area, and everything lands in your hosted Bugline dashboard.
Main Features
Feedback & Bug Reporting
A floating widget lets users write a note, pick a category, and submit feedback or bug reports directly from your app.
Automatic Error Capture
Optional listeners catch unhandled exceptions and promise rejections and forward them as reports with the stack trace attached.
Screenshots & Annotations
Users can capture the page, select a specific element, or draw a highlight box to show developers exactly where the issue is.
AI Page Assistant
An inspect mode where users click any element to get a plain-language description of its role, action, and page context.
Automatic Device Context
Every report includes the page path, browser, OS, screen resolution, language, user agent, and any global metadata you set.
Web & React Native
A browser SDK for React, Next.js, Vue, or plain JS, plus a separate React Native / Expo SDK for element-based mobile audits.
@xtatistix/bugline-web, and the React Native / Expo SDK ships separately as @xtatistix/bugline-sdk with its own component-based API.Quick Start
Get Bugline running in your web application in under two minutes.
@xtatistix/bugline-web). For React Native / Expo, use the separate @xtatistix/bugline-sdk package—see React Native (Mobile).1. Install the SDK
Add the Bugline Web SDK to your project via npm, yarn, or pnpm:
npm install @xtatistix/bugline-web2. Initialize Bugline
Import the named Bugline export and call init() once at the entry point of your application (such as index.js, main.js, or _app.js):
import { Bugline } from class="token-string">'@xtatistix/bugline-web';
Bugline.init({
apiKey: class="token-string">'bl_your_api_key_here', class=class="token-string">"token-comment">// Required
endpoint: class="token-string">'https:class="token-comment">//your-bugline-domain.com/api/reports', // Optional
theme: class="token-string">'dark' class=class="token-string">"token-comment">// Optional: class="token-string">'dark' or class="token-string">'light'
});Minimum Configuration Explained
apiKey: (Required) The API key for your app, generated in the Bugline dashboard. It is sent as thex-api-keyheader on every report.endpoint: (Optional) The URL of your hosted Bugline reports API. Defaults to'/api/reports', which is convenient when the SDK runs on the same origin as your dashboard.theme: (Optional) Widget appearance:'dark'or'light'. Defaults to'dark'.
import { Bugline }—not a default import. Once init() runs, the floating widget mounts automatically unless you set showWidget: false.apiKey. Without it, init() logs a warning and reports are rejected by the server with a 401.Web Integration Guide
Bugline is a framework-agnostic browser SDK. It guards every access to window and document, so it is safe to import in server-rendered builds and only mounts the widget in the browser.
Supported Frameworks
- React: Call
Bugline.init()once in a top-level effect. - Next.js: Initialize from a Client Component; the SDK no-ops during SSR and mounts on the client.
- Vue: Initialize once when the root app mounts.
- Vanilla JavaScript: Import the module (or bundle it) and call
init()on load.
Initialization Example (Next.js App Router)
Initialize Bugline inside a Client Component so it only runs in the browser:
class="token-string">"use client";
import { useEffect } from class="token-string">'react';
import { Bugline } from class="token-string">'@xtatistix/bugline-web';
export function BuglineProvider({ children }) {
useEffect(() => {
Bugline.init({
apiKey: class="token-string">'bl_your_api_key_here',
endpoint: class="token-string">'https:class="token-comment">//your-bugline-domain.com/api/reports',
environment: class="token-string">'production'
});
}, []);
return children;
}Opening the Feedback Widget
By default a floating trigger button sits in the bottom-right corner. To open the panel from your own UI, call Bugline.open() (and Bugline.close() to dismiss it):
const handleFeedbackClick = () => {
Bugline.open();
};showWidget: false to init() to hide the default floating button, then call Bugline.open() yourself.Sending a Report Programmatically
You can submit feedback or bug reports from anywhere in your code without the widget:
Bugline.sendReport({
text: class="token-string">'Checkout button is unresponsive on click',
category: class="token-string">'bug', class=class="token-string">"token-comment">// Optional: class="token-string">'feedback' | class="token-string">'bug' | class="token-string">'ui'
severity: class="token-string">'warning', class=class="token-string">"token-comment">// Optional: class="token-string">'info' | class="token-string">'warning' | class="token-string">'error'
metadata: { cartTotal: 129.99 }
});Attaching Global Metadata
Pass a metadata object to init() to merge the same diagnostic keys into every report the SDK sends—useful for user tiers, organizations, or release tags:
Bugline.init({
apiKey: class="token-string">'bl_your_api_key_here',
metadata: {
plan: class="token-string">'Pro',
organization: class="token-string">'Acme Inc',
releaseTag: class="token-string">'v1.4.2'
}
});metadata yourself.Error Reporting
Bugline collects errors two ways: automatically through global listeners, and manually through the captureException and sendReport methods. All of them funnel into the same request flow.
Automatic Error Capture
When captureErrors is enabled (it defaults to true), the SDK attaches listeners for the window error and unhandledrejection events. Each caught error is sent as a report with severity: 'error' and category: 'bug', including the message, source file, line/column, and stack trace in metadata.
Bugline.init({
apiKey: class="token-string">'bl_your_api_key_here',
captureErrors: true class=class="token-string">"token-comment">// set to false to opt out of global error listeners
});Capturing a Handled Exception
Inside a try/catch, forward a caught error along with any extra context:
try {
executeRiskyOperation();
} catch (error) {
Bugline.captureException(error, {
additionalContext: class="token-string">'Failed during payment processing'
});
}How Reports Are Sent
Every report—whether from the widget, automatic capture, or a manual call—is POSTed to your configured endpoint as JSON, authenticated with the x-api-key header. The SDK enriches the body with the current page path, browser, OS, app version, environment, resolution, language, and user agent before sending.
POST /api/reports
Headers:
Content-Type: application/json
x-api-key: bl_your_api_key_here
Body:
{
class="token-string">"text": class="token-string">"Unhandled error: Cannot read properties of undefined",
class="token-string">"screen": class="token-string">"/checkout",
class="token-string">"deviceModel": class="token-string">"Chrome (128)",
class="token-string">"osVersion": class="token-string">"Windows 10/11",
class="token-string">"appVersion": class="token-string">"1.0.0",
class="token-string">"environment": class="token-string">"production",
class="token-string">"sdkVersion": class="token-string">"web-sdk-1.0.0",
class="token-string">"severity": class="token-string">"error",
class="token-string">"category": class="token-string">"bug",
class="token-string">"metadata": {
class="token-string">"userAgent": class="token-string">"...",
class="token-string">"resolution": class="token-string">"1920x1080",
class="token-string">"language": class="token-string">"en-US",
class="token-string">"errorStack": class="token-string">"..."
}
}x-api-key against your registered apps and responds with { "id": "..." } and a 201 status. An invalid or missing key returns 401.React Native (Mobile)
Bugline ships a separate SDK for React Native and Expo apps, published as @xtatistix/bugline-sdk. It is an element-based audit SDK: testers enter a selection mode, tap a registered UI element (drilling into nested children or parents when targets overlap), write a required note, queue it locally, and submit the collected notes to your API as plain JSON.
@xtatistix/bugline-web) uses the imperative Bugline.init() API. The mobile SDK (@xtatistix/bugline-sdk) is component-based—createAuditClient() plus <AuditProvider>. They are not interchangeable.Requirements
The SDK declares react >= 18 and react-native >= 0.73 as peer dependencies, and works with Expo.
1. Install
Install the SDK and make sure the React Native peer dependencies are present:
npm install @xtatistix/bugline-sdk
# peer dependencies (already present in most RN/Expo apps)
npm install react react-native2. Create a Client and Wrap Your App
Create an audit client with createAuditClient(), wrap your tree in <AuditProvider> and <AuditSelectionRoot>, mark the elements you want to be selectable with stable <AuditSelectable> ids, and drop an <AuditWidget> to expose the floating audit button:
import {
AuditProvider,
AuditSelectable,
AuditSelectionRoot,
AuditWidget,
createAuditClient,
} from class="token-string">'@xtatistix/bugline-sdk';
const audit = createAuditClient({
apiKey: class="token-string">'project-api-key',
endpoint: class="token-string">'https:class="token-comment">//your-domain.com/api/audit/issues',
environment: class="token-string">'staging',
projectId: class="token-string">'mobile-app',
sdkVersion: class="token-string">'0.1.0',
reporter: { id: class="token-string">'user-123', role: class="token-string">'tester' },
privacy: { enabled: true },
});
export function App() {
return (
<AuditProvider client={audit}>
<AuditSelectionRoot>
<AuditSelectable id=class="token-string">"top-bar" kind=class="token-string">"navigation">
<TopBar />
</AuditSelectable>
<AuditSelectable id=class="token-string">"save-button" kind=class="token-string">"button">
<Button title=class="token-string">"Save" />
</AuditSelectable>
<AuditWidget screenName=class="token-string">"Profile" />
</AuditSelectionRoot>
</AuditProvider>
);
}Client Options
createAuditClient(config) accepts:
| Option | Type | Description |
|---|---|---|
apiKey | string | Project API key. Sent as the x-audit-api-key header when configured. |
endpoint | string | Your HTTP endpoint that receives the JSON audit payloads. |
environment | string | Environment label attached to the payload (e.g. 'staging'). |
projectId | string | Identifier for the mobile project, included in every payload. |
sdkVersion | string | Version string recorded with each submitted note. |
reporter | object | Who is filing the notes, e.g. { id, role }. |
privacy | object | Privacy masking config, e.g. { enabled: true }. On by default. |
How Element Auditing Works
AuditSelectionRootmeasures every registeredAuditSelectableelement.AuditSelectionOverlayoutlines selectable elements while selection mode is active.- Tapping a point selects the smallest matching child; a parent/child switcher moves between nested targets (e.g.
SaveText → SaveButton → TopBar). - Confirming a target opens a required note flow; category and severity are optional.
- Saved notes stay in a local draft audit session; audit-only breadcrumbs track select, save, delete, and submit events.
- Submitting the session sends all queued notes together.
How Notes Are Sent
The SDK does not talk to Firebase directly—the built-in HttpAuditTransport posts JSON to your endpoint, and the recommended boundary is Mobile SDK → API route handler → Firebase. Each request sends:
content-type: application/jsonx-audit-api-keywhenapiKeyis configured- a JSON body built by
buildElementNotePayload() - a batch body
{ session, notes: [...] }when several saved notes are submitted together
Each element note payload is plain JSON with these fields:
{
projectId, environment, sdkVersion, screenName,
selectedElement, ancestors, children,
note, category, severity,
sessionId, breadcrumbs, createdAt
}app/api/reports/route.js) already accepts the x-audit-api-key header and batch { notes: [...] } bodies, so one backend can serve both the web and mobile SDKs.Privacy Masking
Masking is enabled by default in the payload builder and redacts sensitive data before JSON leaves the device: keys containing token, authorization, cookie, secret, password, email, or phone, plus email addresses, phone-like numbers, and bearer/JWT/API-token values. Override the defaults via createAuditClient({ privacy: ... }).
Exporting Notes
The SDK also exposes buildMarkdown() and buildDocx() helpers to turn a collected audit session into a Markdown or Word document for sharing.
Widget & Reporting Tools
When showWidget is enabled (the default), Bugline mounts a floating trigger button in the bottom-right corner. Clicking it slides out a feedback panel with the tools below.
Note & Category
Users type a description and pick a category chip (such as Bug, Feedback, or UI) before submitting.
Select Element
An inspect mode where the user taps any element on the page to pin the report to a specific DOM node.
Draw Area
Users drag a highlight box over the page to outline the exact region where the problem appears.
Screenshot Capture
The widget renders the page to an image with html2canvas (loaded on demand) and attaches it to the report.
Offline Drafts
Reports the user creates are saved locally under the bugline_draft_reports key in localStorage. A badge on the trigger button shows how many drafts are pending, and users can send them individually or all at once. Drafts that fail to send are kept and marked as failed so they can be retried.
html2canvas-pro from a CDN the first time a capture is requested. If your site enforces a strict Content Security Policy, allow that script source or disable screenshot capture in your integration.AI Assistant
The AI Assistant is an in-page inspect mode. A second floating button (the ring-shaped icon above the main widget) turns any element on the page into a plain-language explanation of what it is and what it does—handy for users describing an issue and for teammates unfamiliar with the UI.
How It Works
- The user clicks the AI Assistant button above the main widget trigger.
- They click any element—a button, link, input, or section.
- A card appears describing the element's role, its likely action, and where it sits on the page (navigation, sidebar, form, footer, or main content).
Context-Aware Descriptions
The assistant reads the element's tag, text, ARIA labels, and surrounding headings to explain its purpose in context.
Layout Location Detection
It detects whether the element lives in a nav bar, sidebar, form, card, table, or footer, and phrases the explanation accordingly.
Local Analysis with Optional Backend
The assistant first attempts to POST the selected element's tag, id, class, a trimmed outerHTML, and inner text to {endpoint}/ai-analyze. If that request fails or the endpoint isn't available, it falls back to a built-in heuristic engine that runs entirely in the browser—so the assistant works out of the box even without a hosted analysis endpoint.
ai-analyze endpoint is optional. Out of the box the assistant produces its descriptions locally from the page DOM; wiring up a backend at {endpoint}/ai-analyze lets you return richer, model-generated explanations.Configuration
Configure the SDK by passing an options object to Bugline.init(). Only apiKey is required; every other option has a sensible default.
| Option | Type | Default | Description |
|---|---|---|---|
apiKey | string | Required | The API key for your app from the Bugline dashboard. Sent as the x-api-key header on every report. |
endpoint | string | '/api/reports' | URL the SDK posts reports to. Use your hosted dashboard's full URL when the SDK runs on a different origin. |
appVersion | string | '1.0.0' | The version of your client application, attached to each report. |
environment | string | 'production' | Deployment environment label (e.g. 'development', 'staging', 'production'). |
captureErrors | boolean | true | Automatically capture unhandled errors and promise rejections as reports. |
showWidget | boolean | true | Mount the floating feedback button and side panel. Set to false to drive the widget yourself. |
theme | string | 'dark' | Widget appearance: 'dark' or 'light'. |
metadata | object | {} | Key-value pairs merged into the metadata of every report the SDK sends. |
--bl-primary-color and --bl-accent-color in your own stylesheet.API Reference
The Bugline named export exposes the following methods. Import it with import { Bugline } from '@xtatistix/bugline-web'. (This is the web API; the React Native SDK has a different, component-based API—see React Native (Mobile).)
Bugline.init(options)
Initializes the SDK, wires up error capture, and mounts the widget. Call it once at startup.
- Parameters:
options(Object) — see the Configuration table.apiKeyis required. - Returns:
void
Bugline.init({ apiKey: class="token-string">'bl_your_api_key_here' });Bugline.open()
Opens the feedback side panel.
- Returns:
void
Bugline.open();Bugline.close()
Closes the feedback side panel.
- Returns:
void
Bugline.close();Bugline.sendReport(report)
Sends a feedback or bug report programmatically. The SDK enriches it with device, browser, and page context before posting.
- Parameters:
report(Object) —text(string), and optionallyscreen,deviceModel,osVersion,screenshotUrl,severity('info'|'warning'|'error', default'info'),category(default'feedback'), andmetadata(Object). - Returns:
Promise— resolves with the server response (e.g.{ id }), rejects on failure.
Bugline.sendReport({
text: class="token-string">'Save button overlaps the input on narrow screens',
category: class="token-string">'ui',
severity: class="token-string">'warning',
metadata: { viewport: window.innerWidth }
});Bugline.captureException(error, extraMetadata?)
Sends a caught error as a report with severity: 'error' and category: 'bug', attaching the stack trace.
- Parameters:
error(Error or value) and optionalextraMetadata(Object) merged into the report metadata. - Returns:
Promise
try {
doSomething();
} catch (err) {
Bugline.captureException(err, { screen: class="token-string">'/settings' });
}Best Practices
A few recommendations for running Bugline cleanly in production.
Set the Environment
Pass an accurate environment to init() ('development', 'staging', or 'production'). It's attached to every report so you can filter dashboard reports by where they came from.
Attach Context with Metadata
Provide a metadata object at init() with details like the current user's plan, organization, or a release tag. Those keys are merged into every report, which makes triage in the dashboard much faster than device data alone.
Protect Your API Key
The apiKey ships to the browser, so treat it as a publishable client key: scope it to a single app and rotate it from the dashboard if it's misused. Reports are only accepted for the app the key belongs to.
Screenshots & CSP
Screenshot capture loads html2canvas-pro from a CDN on first use. If you run a strict Content Security Policy, allow that script origin—otherwise capture silently falls back to sending the report without an image.
Errors in Production
Keep captureErrors enabled in production so unhandled exceptions and promise rejections are reported automatically. Disable it only if another error-monitoring tool already owns those global listeners.
Troubleshooting
Solutions to the most common integration issues.
Confirm init() actually runs in the browser (not only on the server), that showWidget isn't set to false, and that no other element with a very high z-index covers the bottom-right corner. In Next.js, call init() from a Client Component or inside a useEffect.
The apiKey is missing or invalid. Make sure you passed apiKey to init() and that it matches an app registered in your Bugline dashboard. The key is sent as the x-api-key header, and the server rejects unknown keys with 401.
Bugline is a named export. Use import { Bugline } from '@xtatistix/bugline-web', not a default import. A default import resolves to undefined.
When the SDK and your dashboard are on different origins, the reports endpoint must allow your site's origin and the x-api-key header. Point endpoint at your hosted dashboard's full /api/reports URL and confirm its CORS configuration permits cross-origin POST requests.
The SDK guards browser globals, so importing it during SSR is safe. This error usually means your own code touches window at module scope—move Bugline.init() into a client-only lifecycle such as useEffect.
Capture loads html2canvas-pro from a CDN on first use. If a Content Security Policy or network block prevents that script from loading, the report is still sent but without the image. Allowlist the CDN script source to enable captures.
FAQ
Frequently asked developer questions regarding integration, features, and security scopes.
Yes. Import the named Bugline export and call Bugline.init() once in a top-level effect. There's no React-specific package—the same browser SDK works everywhere.
Yes. The SDK guards every access to window and document, so it's safe to import in SSR builds. Call init() from a Client Component (or inside useEffect) so the widget mounts on the client.
Yes. React Native and Expo apps use a separate package, @xtatistix/bugline-sdk, which is a component-based element-audit SDK (createAuditClient + AuditProvider). It's different from the web SDK—see the React Native (Mobile) guide. There is no Flutter, native Android, or native iOS package.
Yes. The widget can render the page to an image using html2canvas (loaded on demand) and attach it to the report. Users can also select a specific element or draw a highlight box to pinpoint the issue.
It's an inspect mode: users click an element and get a plain-language description of its role and page context. It tries an optional {endpoint}/ai-analyze backend and otherwise falls back to a local heuristic engine that runs in the browser.
Each report carries your message, category, and severity plus automatically collected context: the page path, browser name/version, OS, screen resolution, language, user agent, app version, environment, and any global metadata you configured.