Widget Installation
The BugPin widget is a lightweight bug reporting tool (under 50KB gzipped) that can be added to any website with a single script tag or installed as an npm package. It captures screenshots, lets users annotate issues with drawing tools, and submits reports to your self-hosted BugPin server. This guide covers both installation methods and includes framework-specific examples for React, Next.js, Vue, Angular, and .NET.
Installation Methods
Method 1: Script Tag (Recommended)
Add the following script tag to your HTML, just before the closing </body> tag:
<script
src="https://your-bugpin-server.com/widget.js"
data-api-key="your-project-api-key"
async
></script>
Replace:
your-bugpin-server.comwith your BugPin server URLyour-project-api-keywith the API key from your project settings
The widget automatically detects the server URL from the script source - no additional configuration needed!
Method 2: npm Package
Install the widget as an npm package for use with bundlers (webpack, Vite, etc.):
npm install @arantic/bugpin-widget
Then import and initialize in your application:
import BugPin from '@arantic/bugpin-widget';
await BugPin.init({
apiKey: 'your-project-api-key',
serverUrl: 'https://your-bugpin-server.com'
});
When using the npm package, you must provide the serverUrl parameter because the widget code is bundled into your application. Unlike the script tag method where the server URL is automatically detected from the script source, npm usage requires explicit configuration.
TypeScript Support:
import BugPin from '@arantic/bugpin-widget';
await BugPin.init({
apiKey: 'your-project-api-key',
serverUrl: 'https://your-bugpin-server.com'
});
Note: Widget appearance (theme, position, colors, button text) is configured in the BugPin Admin Console and automatically fetched by the widget. You only need to provide the API key and server URL in your code.
Using Environment Variables (Recommended):
For production applications, store your API key in environment variables:
// Vite (React, Vue)
await BugPin.init({
apiKey: import.meta.env.VITE_BUGPIN_API_KEY,
serverUrl: import.meta.env.VITE_BUGPIN_SERVER_URL
});
// Next.js
await BugPin.init({
apiKey: process.env.NEXT_PUBLIC_BUGPIN_API_KEY,
serverUrl: process.env.NEXT_PUBLIC_BUGPIN_SERVER_URL
});
Then add to your .env file:
# Vite (React, Vue) - .env
VITE_BUGPIN_API_KEY=your-project-api-key
VITE_BUGPIN_SERVER_URL=https://bugpin.example.com
# Next.js - .env.local
NEXT_PUBLIC_BUGPIN_API_KEY=your-project-api-key
NEXT_PUBLIC_BUGPIN_SERVER_URL=https://bugpin.example.com
Fetch from Backend:
For added security, fetch the configuration from your backend:
async function initBugPin() {
const response = await fetch('/api/bugpin-config');
const config = await response.json();
await BugPin.init({
apiKey: config.apiKey,
serverUrl: config.serverUrl
});
}
initBugPin();
Programmatic Initialization
For more control (e.g., initializing after user login), you can initialize the widget programmatically:
<script src="https://bugpin.example.com/widget.js" async></script>
<script>
window.addEventListener('load', async function() {
if (window.BugPin) {
await window.BugPin.init({
apiKey: 'your-project-api-key',
serverUrl: 'https://bugpin.example.com' // Optional - auto-detected from script src
});
}
});
</script>
When loading via script tag, serverUrl is optional and will be automatically detected from the script's source URL. Only specify it if you need to override the default behavior.
JavaScript API
The widget exposes a global BugPin object for programmatic control. Use this when you want to:
- Custom trigger: Hide the default button and open the reporter from your own UI (e.g., a "Report Bug" menu item)
- Contextual triggers: Automatically open the reporter after an error occurs
- Keyboard shortcuts: Trigger the reporter with a hotkey
Available methods:
BugPin.init(config)
Initialize the widget by telling it which project to connect to. The widget then fetches all appearance settings (position, theme, colors) from the Admin Console automatically.
Parameters:
apiKey(required): Your project's API keyserverUrl(optional when using script tag, required for npm package): BugPin server URL
Returns: Promise<void> - Resolves when the widget is initialized and config is fetched from the server.
await BugPin.init({
apiKey: 'your-project-api-key',
serverUrl: 'https://bugpin.example.com' // Auto-detected if loaded via script tag
});
BugPin.open()
Open the bug report dialog programmatically.
BugPin.open();
BugPin.close()
Close the bug report dialog programmatically.
BugPin.close();
Example: Custom Trigger Button
// Your own "BugPin" button in your app's UI
document.getElementById('bugpin-launcher-btn').addEventListener('click', () => {
BugPin.open();
});
// Or with a keyboard shortcut (Ctrl+Shift+B)
document.addEventListener('keydown', (e) => {
if (e.ctrlKey && e.shiftKey && e.key === 'B') {
BugPin.open();
}
});
Single Page Applications (SPA)
The widget works with SPAs built with React, Vue, Angular, and other frameworks. It automatically detects route changes and captures the current URL.
React / Vite (npm)
import { useEffect } from 'react';
import BugPin from '@arantic/bugpin-widget';
function App() {
useEffect(() => {
BugPin.init({
apiKey: import.meta.env.VITE_BUGPIN_API_KEY,
serverUrl: import.meta.env.VITE_BUGPIN_SERVER_URL
});
return () => {
const widget = document.getElementById('bugpin-widget');
if (widget) widget.remove();
};
}, []);
return <div>Your app content</div>;
}
Note: While BugPin.init() returns a Promise, you don't need to await it in useEffect. The widget initializes in the background and appears when ready.
React / Vite (script tag)
import { useEffect } from 'react';
function App() {
useEffect(() => {
const script = document.createElement('script');
script.src = import.meta.env.VITE_BUGPIN_SERVER_URL + '/widget.js';
script.async = true;
script.setAttribute('data-api-key', import.meta.env.VITE_BUGPIN_API_KEY);
document.body.appendChild(script);
return () => {
const widget = document.getElementById('bugpin-widget');
if (widget) widget.remove();
};
}, []);
return <div>Your app content</div>;
}
Next.js (npm)
import { useEffect } from 'react';
import BugPin from '@arantic/bugpin-widget';
function App() {
useEffect(() => {
BugPin.init({
apiKey: process.env.NEXT_PUBLIC_BUGPIN_API_KEY,
serverUrl: process.env.NEXT_PUBLIC_BUGPIN_SERVER_URL
});
return () => {
const widget = document.getElementById('bugpin-widget');
if (widget) widget.remove();
};
}, []);
return <div>Your app content</div>;
}
Next.js (script tag)
import { useEffect } from 'react';
function App() {
useEffect(() => {
const script = document.createElement('script');
script.src = process.env.NEXT_PUBLIC_BUGPIN_SERVER_URL + '/widget.js';
script.async = true;
script.setAttribute('data-api-key', process.env.NEXT_PUBLIC_BUGPIN_API_KEY);
document.body.appendChild(script);
return () => {
const widget = document.getElementById('bugpin-widget');
if (widget) widget.remove();
};
}, []);
return <div>Your app content</div>;
}
Vue / Nuxt (npm)
<script setup>
import { onMounted, onUnmounted } from 'vue';
import BugPin from '@arantic/bugpin-widget';
onMounted(() => {
BugPin.init({
apiKey: import.meta.env.VITE_BUGPIN_API_KEY,
serverUrl: import.meta.env.VITE_BUGPIN_SERVER_URL
});
});
onUnmounted(() => {
const widget = document.getElementById('bugpin-widget');
if (widget) widget.remove();
});
</script>
Vue / Nuxt (script tag)
<script setup>
import { onMounted, onUnmounted } from 'vue';
onMounted(() => {
const script = document.createElement('script');
script.src = import.meta.env.VITE_BUGPIN_SERVER_URL + '/widget.js';
script.async = true;
script.setAttribute('data-api-key', import.meta.env.VITE_BUGPIN_API_KEY);
document.body.appendChild(script);
});
onUnmounted(() => {
const widget = document.getElementById('bugpin-widget');
if (widget) widget.remove();
});
</script>
.NET Examples
ASP.NET Core / Razor Pages
Add to your _Layout.cshtml or _Host.cshtml:
<!-- In Shared/_Layout.cshtml -->
<!DOCTYPE html>
<html>
<head>
<!-- Your head content -->
</head>
<body>
@RenderBody()
<!-- BugPin Widget -->
<script
src="https://bugpin.example.com/widget.js"
data-api-key="@Configuration["BugPin:ApiKey"]"
async
></script>
</body>
</html>
Configure the API key in appsettings.json:
{
"BugPin": {
"ApiKey": "your-project-api-key",
"ServerUrl": "https://bugpin.example.com"
}
}
Blazor Server
Add to Pages/_Host.cshtml:
<!DOCTYPE html>
<html>
<head>
<!-- Your head content -->
</head>
<body>
<app>@(await Html.RenderComponentAsync<App>(RenderMode.ServerPrerendered))</app>
<!-- BugPin Widget -->
<script
src="@Configuration["BugPin:ServerUrl"]/widget.js"
data-api-key="@Configuration["BugPin:ApiKey"]"
async
></script>
</body>
</html>
Blazor WebAssembly
Add to wwwroot/index.html:
<!DOCTYPE html>
<html>
<head>
<!-- Your head content -->
</head>
<body>
<div id="app">Loading...</div>
<!-- BugPin Widget -->
<script
src="https://bugpin.example.com/widget.js"
data-api-key="your-project-api-key"
async
></script>
</body>
</html>
For dynamic initialization in Blazor, use JavaScript interop:
// Services/BugPinService.cs
public class BugPinService
{
private readonly IJSRuntime _js;
public BugPinService(IJSRuntime js)
{
_js = js;
}
public async Task InitializeAsync(string apiKey, string serverUrl)
{
await _js.InvokeVoidAsync("BugPin.init", new
{
apiKey = apiKey,
serverUrl = serverUrl
});
}
public async Task OpenAsync()
{
await _js.InvokeVoidAsync("BugPin.open");
}
}
Content Security Policy (CSP)
If your site uses a Content Security Policy, you need to add directives to allow the BugPin widget to function properly.
Required Directives
Add these directives to your existing CSP configuration:
script-src 'self' https://your-bugpin-server.com;
connect-src 'self' https://your-bugpin-server.com;
style-src 'self' 'unsafe-inline';
img-src 'self' data:;
media-src 'self' data:;
Directive Breakdown:
script-src: Allows the widget JavaScript to load from your BugPin serverconnect-src: Allows API requests to submit reports and fetch configurationstyle-src: Allows inline styles used by the widget (Shadow DOM encapsulation)img-src: Allows screenshots captured as data URLsmedia-src: Allows video recordings/uploads as data URLs
Implementation Methods
You can add CSP directives using either HTTP headers (recommended) or HTML meta tags.
Method 1: HTTP Response Headers (Recommended)
Add the Content-Security-Policy header to your server responses. The implementation varies by server:
Apache (.htaccess or httpd.conf):
Header set Content-Security-Policy "script-src 'self' https://your-bugpin-server.com; connect-src 'self' https://your-bugpin-server.com; style-src 'self' 'unsafe-inline'; img-src 'self' data:; media-src 'self' data:;"
Nginx (nginx.conf):
add_header Content-Security-Policy "script-src 'self' https://your-bugpin-server.com; connect-src 'self' https://your-bugpin-server.com; style-src 'self' 'unsafe-inline'; img-src 'self' data:; media-src 'self' data:;";
Node.js / Express:
app.use((req, res, next) => {
res.setHeader(
'Content-Security-Policy',
"script-src 'self' https://your-bugpin-server.com; connect-src 'self' https://your-bugpin-server.com; style-src 'self' 'unsafe-inline'; img-src 'self' data:; media-src 'self' data:;"
);
next();
});
Next.js (next.config.js):
module.exports = {
async headers() {
return [
{
source: '/:path*',
headers: [
{
key: 'Content-Security-Policy',
value: "script-src 'self' https://your-bugpin-server.com; connect-src 'self' https://your-bugpin-server.com; style-src 'self' 'unsafe-inline'; img-src 'self' data:; media-src 'self' data:;"
}
]
}
];
}
};
ASP.NET Core (Startup.cs or Program.cs):
app.Use(async (context, next) =>
{
context.Response.Headers.Add(
"Content-Security-Policy",
"script-src 'self' https://your-bugpin-server.com; connect-src 'self' https://your-bugpin-server.com; style-src 'self' 'unsafe-inline'; img-src 'self' data:; media-src 'self' data:;"
);
await next();
});
Django (settings.py with django-csp):
CSP_SCRIPT_SRC = ["'self'", "https://your-bugpin-server.com"]
CSP_CONNECT_SRC = ["'self'", "https://your-bugpin-server.com"]
CSP_STYLE_SRC = ["'self'", "'unsafe-inline'"]
CSP_IMG_SRC = ["'self'", "data:"]
CSP_MEDIA_SRC = ["'self'", "data:"]
Method 2: HTML Meta Tag
If you cannot modify server headers, add a <meta> tag to your HTML <head>:
<meta http-equiv="Content-Security-Policy"
content="script-src 'self' https://your-bugpin-server.com;
connect-src 'self' https://your-bugpin-server.com;
style-src 'self' 'unsafe-inline';
img-src 'self' data:;
media-src 'self' data:;">
HTTP headers are preferred over meta tags. Some CSP directives (like frame-ancestors) don't work in meta tags.
Merging with Existing CSP
If you already have a CSP, merge the BugPin directives with your existing ones:
Example - Existing CSP:
script-src 'self' https://cdn.example.com;
img-src 'self' https://images.example.com;
Merged with BugPin:
script-src 'self' https://cdn.example.com https://your-bugpin-server.com;
img-src 'self' https://images.example.com data:;
connect-src 'self' https://your-bugpin-server.com;
style-src 'self' 'unsafe-inline';
media-src 'self' data:;