blinker-sdk
v2.2.1
Published
Universal JavaScript SDK for error tracking and event capture - works with any framework
Maintainers
Readme
Blinker SDK
Universal JavaScript SDK for error tracking and event capture. Works with any framework - React, Vue, Angular, Next.js, or Vanilla JavaScript.
Features
- Zero dependencies - Lightweight and fast
- Universal - Works in any JavaScript environment
- Automatic error capture - Catches global errors and unhandled rejections
- Smart filtering - Ignores common non-bugs (401, 403, ResizeObserver) by default
- Event batching - Efficient sending with configurable batch size
- Offline support - Persists events when offline, sends when back online
- Deduplication - Prevents flood of identical errors
- Rate limiting - Protects against excessive event sending
- Sampling - Sample events for high-traffic apps
- Session tracking - Automatic session management with pageview counting
- User context - Identify users and attach custom context
- Feedback widget - Interactive avatar with server-driven questions for user feedback
- Multiple formats - ESM, CommonJS, and UMD (CDN)
- Type-safe - Full TypeScript support
Installation
npm / yarn / pnpm
npm install blinker-sdk
# or
yarn add blinker-sdk
# or
pnpm add blinker-sdkCDN
<script src="https://unpkg.com/blinker-sdk/dist/blinker.min.js"></script>
<script>
const { blinker } = BlinkerSDK;
blinker.init({ token: 'your-token' });
</script>Quick Start
import { blinker } from 'blinker-sdk';
blinker.init({ token: 'your-blinker-token' });
// That's it! Errors are now automatically captured.
// Batches events for efficient sending
// Works offline with localStorage persistence
// Deduplicates repeated errors
// Ignores 401/403 errors by default
// Filters out common browser noiseUsage
Initialization
import { blinker } from 'blinker-sdk';
// Minimal setup
blinker.init({ token: 'your-blinker-token' });
// Full options
blinker.init({
token: 'your-blinker-token',
captureErrors: true,
captureRejections: true,
enableBatching: true,
batchSize: 10,
flushInterval: 5000,
enableOfflineStorage: true,
maxStoredEvents: 100,
enableDeduplication: true,
deduplicationWindow: 60000,
maxEventsPerMinute: 100,
sampleRate: 1.0,
filters: {
ignoreHttpCodes: [401, 403],
captureAll: false
},
widget: {
enabled: true,
position: 'bottom-right',
delay: 2000,
maxShowsPerSession: 2,
cooldownMinutes: 30
},
debug: false
});User Identification
blinker.identify('user-123');
blinker.identify('user-123', {
name: 'John Doe',
email: '[email protected]',
plan: 'pro'
});
blinker.clearContext();Custom Context
blinker.setContext('environment', 'production');
blinker.setContext('appVersion', '2.1.0');
const context = blinker.getContext();
blinker.clearContext();Track Custom Events
blinker.track('click', 'Button clicked');
blinker.track('purchase', 'User made a purchase', {
productId: '12345',
amount: 99.99
});
blinker.trackPageView();
blinker.trackPageView('Home Page', { section: 'hero' });Manual Error Capture
try {
// Your code
} catch (error) {
blinker.captureError(error, { context: 'checkout' });
}Error Filters
The SDK ignores common non-bug errors by default:
401and403HTTP errorsResizeObserver looperrorsScript error(cross-origin)
// Capture everything
blinker.init({
token: 'your-token',
filters: { captureAll: true }
});
// Custom HTTP code filters
blinker.init({
token: 'your-token',
filters: { ignoreHttpCodes: [401, 403, 404, 429] }
});
// Ignore specific error types
blinker.init({
token: 'your-token',
filters: { ignoreErrorTypes: ['TypeError', 'SyntaxError'] }
});
// Ignore message patterns
blinker.init({
token: 'your-token',
filters: {
ignoreMessagePatterns: ['ChunkLoadError', 'Network request failed']
}
});Session Information
const sessionId = blinker.getSessionId();
const session = blinker.getSession();
// { sessionId, startTime, pageViews, duration }Queue Control
const pending = blinker.getQueueSize();
await blinker.flush();Check SDK Status
blinker.isInitialized();
blinker.getConfig();
blinker.getFilters();Cleanup
blinker.destroy();Feedback Widget
The SDK includes an interactive feedback widget that collects user context when errors occur. An animated avatar appears after an error, and when clicked, opens a dialog with questions configured by your team.
How It Works
- Error is captured by the SDK
- SDK sends the event to the server (
POST /events) - Server evaluates widget rules and returns relevant questions
- If widget should show: avatar appears after configurable delay
- User clicks the avatar to open the feedback dialog
- Questions are displayed based on the error type (text, yes/no, select)
- User submits answers, which are sent as feedback (
POST /feedback)
Enabling the Widget
blinker.init({
token: 'your-token',
widget: {
enabled: true
}
});Widget Configuration
blinker.init({
token: 'your-token',
widget: {
enabled: true,
position: 'bottom-right', // 'bottom-right' | 'bottom-left'
delay: 2000, // ms before avatar appears
maxShowsPerSession: 2, // max widget shows per session
cooldownMinutes: 30, // cooldown between shows
theme: {
primary: '#6366f1',
background: '#ffffff',
text: '#1f2937',
accent: '#10b981'
},
messages: {
greeting: 'Oops! Something went wrong.',
subtext: 'Can you help us understand what happened?',
thankYou: 'Thanks! We\'re looking into it.',
buttonSend: 'Send',
buttonSkip: 'Not now'
}
}
});Server-Driven Questions
Questions are configured in the Blinker dashboard and served dynamically based on the error type. The server matches error characteristics (HTTP codes, message patterns, error types) against rules to determine which questions to show.
Question types:
| Type | Description | Example |
|------|-------------|---------|
| text | Free-text input | "What were you doing?" |
| boolean | Yes/No toggle | "Were you logged in?" |
| select | Dropdown options | "Which browser?" (Chrome, Firefox, Safari...) |
Rule matching: Rules are evaluated by priority. The first matching rule determines which questions appear. If no rule matches, the first two questions are shown as fallback.
Dashboard Configuration
In the Blinker dashboard (/widget), you can:
- Enable/disable the widget (template questions are seeded on first enable)
- Create, edit, and delete questions
- Configure rules that match errors to specific questions
- Set conditions: HTTP codes, HTTP code ranges, message patterns, error types
Widget Lifecycle
Error captured
-> Event sent to server
-> Server returns widget decision: { show: true/false, questions: [...] }
-> If show: true, avatar appears after delay
-> User clicks avatar -> dialog opens with questions
-> User submits -> feedback sent to server
-> Avatar disappears (with cooldown)Rate limiting:
maxShowsPerSessionlimits how many times the widget appears per sessioncooldownMinutesenforces minimum time between shows- State is persisted in
sessionStorage(resets on new tab/window)
Framework Examples
React
import { blinker } from 'blinker-sdk';
blinker.init({
token: process.env.REACT_APP_BLINKER_TOKEN,
widget: { enabled: true }
});
function App() {
useEffect(() => {
blinker.identify(user.id, { name: user.name, email: user.email });
}, [user]);
return <button onClick={() => blinker.track('click', 'CTA clicked')}>Click me</button>;
}Next.js
'use client';
import { useEffect } from 'react';
import { blinker } from 'blinker-sdk';
export default function RootLayout({ children }) {
useEffect(() => {
blinker.init({
token: process.env.NEXT_PUBLIC_BLINKER_TOKEN,
widget: { enabled: true }
});
}, []);
return (
<html>
<body>{children}</body>
</html>
);
}Vue.js
import { createApp } from 'vue';
import { blinker } from 'blinker-sdk';
import App from './App.vue';
blinker.init({
token: import.meta.env.VITE_BLINKER_TOKEN,
widget: { enabled: true }
});
createApp(App).mount('#app');Vanilla JavaScript
<script src="https://unpkg.com/blinker-sdk/dist/blinker.min.js"></script>
<script>
const { blinker } = BlinkerSDK;
blinker.init({ token: 'your-token', widget: { enabled: true } });
blinker.identify('user-123', { name: 'John' });
</script>API Reference
blinker.init(config)
Initialize the SDK.
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| token | string | required | Your Blinker API token |
| captureErrors | boolean | true | Auto-capture window errors |
| captureRejections | boolean | true | Auto-capture unhandled rejections |
| enableBatching | boolean | true | Enable event batching |
| batchSize | number | 10 | Events per batch |
| flushInterval | number | 5000 | Batch flush interval (ms) |
| enableOfflineStorage | boolean | true | Persist events when offline |
| maxStoredEvents | number | 100 | Max offline events |
| enableDeduplication | boolean | true | Enable error deduplication |
| deduplicationWindow | number | 60000 | Dedup window (ms) |
| maxEventsPerMinute | number | 100 | Rate limit (0 = unlimited) |
| sampleRate | number | 1.0 | Sample rate (0.0-1.0) |
| filters | FilterSettings | see below | Error filter configuration |
| widget | WidgetConfig | see below | Feedback widget configuration |
| onBeforeSend | function | undefined | Transform or drop events before sending |
| debug | boolean | false | Enable debug logging |
FilterSettings
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| ignoreHttpCodes | number[] | [401, 403] | HTTP codes to ignore |
| ignoreErrorTypes | string[] | [] | JS error types to ignore |
| ignoreMessagePatterns | string[] | ['ResizeObserver loop', 'Script error'] | Patterns to ignore |
| captureAll | boolean | false | Disable all filters |
WidgetConfig
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| enabled | boolean | false | Enable feedback widget |
| position | string | 'bottom-right' | 'bottom-right' or 'bottom-left' |
| delay | number | 2000 | Delay before showing avatar (ms) |
| maxShowsPerSession | number | 2 | Max shows per session |
| cooldownMinutes | number | 30 | Cooldown between shows (min) |
| theme | WidgetTheme | see defaults | Widget colors |
| messages | WidgetMessages | see defaults | Widget text strings |
blinker.track(type, message, payload?)
Track a custom event. Returns Promise<{ success: boolean, error?: string }>.
blinker.captureError(error, payload?)
Manually capture an error.
blinker.trackPageView(pageName?, payload?)
Track a page view.
blinker.identify(userId, traits?)
Identify the current user.
blinker.setContext(key, value) / blinker.getContext() / blinker.clearContext()
Manage custom context included in all events.
blinker.getSessionId() / blinker.getSession()
Get session tracking data.
blinker.flush()
Force flush all queued events. Returns Promise<void>.
blinker.getQueueSize()
Get number of pending events. Returns number.
blinker.isInitialized() / blinker.getConfig() / blinker.getFilters()
Check SDK state.
blinker.destroy()
Remove error handlers and reset SDK state.
Event Payload
Events sent to the API:
{
"type": "error",
"message": "Error message",
"payload": { "custom": "data" },
"timestamp": "2026-01-15T12:00:00.000Z",
"url": "https://example.com/page",
"userAgent": "Mozilla/5.0...",
"sessionId": "abc123...",
"userId": "user-123",
"userTraits": { "name": "John", "plan": "pro" },
"context": { "appVersion": "2.1.0" },
"count": 5
}TypeScript
Full type definitions included.
import { blinker, BlinkerConfig, FilterSettings, SessionInfo, WidgetConfig } from 'blinker-sdk';
const config: BlinkerConfig = {
token: 'your-token',
enableBatching: true,
sampleRate: 0.5,
widget: { enabled: true },
filters: { ignoreHttpCodes: [401, 403, 404] }
};
blinker.init(config);Multiple Instances
import { Blinker } from 'blinker-sdk';
const frontendErrors = new Blinker();
const backendErrors = new Blinker();
frontendErrors.init({ token: 'frontend-token', widget: { enabled: true } });
backendErrors.init({ token: 'backend-token' });Browser Support
- Chrome 63+
- Firefox 67+
- Safari 12+
- Edge 79+
Uses ES2018 features (async/await, fetch).
License
MIT
