npm install signalbirdsignalbird/browser - Browser
React, Vue, Angular, Svelte and plain JavaScript all use the same client. There is no framework-specific wrapper; all you need is one function call.
Why a separate package?
The reason is not technical, it is security.
The server key (sb_secret_live_…) is secret and cannot be embedded in the client:
once it reaches a browser it belongs to everyone. The server enforces this as a
rule, rejecting a secret key on any request that carries an Origin header
(SECRET_KEY_IN_BROWSER).
The browser uses a public key (sb_public_live_…). That key is not secret; its
safety comes from restriction:
- it can write only from the allowed origins (domains) you define in the panel (your domain and its subdomains are added automatically when the key is created),
- it cannot write the critical level: a
criticalrecord from the browser is stored aserror(critical breaks quiet hours; a key embedded in the client must not be able to ring the team's phones at night), - writing to an unknown channel name opens the channel automatically and silently; there is a cap on automatically opened channels per team.
Client-side code is in everyone's hands. Manage notification rules (who, push or email) in the channel settings; a separate channel for browser errors is recommended.
Install
npm install signalbirdSetup and usage
import { initSignalbird } from 'signalbird/browser'
const sb = initSignalbird({
publicKey: 'sb_public_live_…',
source: 'web',
})
// window.onerror + unhandledrejection
sb.captureErrors('browser')
sb.error('browser', 'the cart could not be updated', { cartId })Records are not sent one by one but in batches (every 3 seconds by default),
and the queue is flushed with sendBeacon as the tab closes: firing dozens of
requests per second out of a user's browser is both slow and expensive.
React / Next.js
'use client'
import { useEffect } from 'react'
import { initSignalbird } from 'signalbird/browser'
export function SignalbirdProvider() {
useEffect(() => {
const sb = initSignalbird({
publicKey: process.env.NEXT_PUBLIC_SIGNALBIRD_DOMAIN_KEY!,
})
return sb.captureErrors()
}, [])
return null
}Vue
import { initSignalbird } from 'signalbird/browser'
const sb = initSignalbird({ publicKey: import.meta.env.VITE_SIGNALBIRD_DOMAIN_KEY })
app.config.errorHandler = (err) => sb.error('browser', String(err))Angular
import { ErrorHandler, Injectable } from '@angular/core'
import { initSignalbird } from 'signalbird/browser'
@Injectable()
export class SignalbirdErrorHandler implements ErrorHandler {
private sb = initSignalbird({ publicKey: environment.signalbirdKey })
handleError(error: unknown) {
this.sb.error('browser', String(error))
}
}Plain JavaScript
<script type="module">
import { initSignalbird } from 'https://cdn.jsdelivr.net/npm/signalbird/dist/browser.mjs'
const sb = initSignalbird({ publicKey: 'sb_public_live_…' })
sb.captureErrors()
</script>Options
| Field | Default | Description |
|---|---|---|
publicKey |
- | Required. Must start with sb_public_live_ |
baseUrl |
https://signalbird.io/api |
If you run your own installation |
source |
- | An origin name added to every record |
flushIntervalMs |
3000 |
How often the queue is flushed |
maxQueue |
50 |
The most records held in memory |
Methods
| Method | Description |
|---|---|
log(key, message, level?, context?) |
The base call |
info · warn · error |
Level shortcuts |
captureErrors(key?) |
Hooks error + unhandledrejection |
flush() |
Sends the queue right away |
destroy() |
Stops the timer and makes a final send |
Version notice
The SDK reports its version on every request: X-Signalbird-Sdk: browser/<version>.
When a newer version is released the API responds with
Signalbird-Sdk-Status: outdated (or unsupported once it is no longer
supported) and the SDK writes a warning once per process (console.warn).
The warning is not an error: the request result does not change.
In the panel, Domains → [domain] → Keys shows which SDK version each key is used with; keys on an old version also get a banner at the top of the panel and a notification. To update:
npm i signalbird@latestError codes
| Code | Meaning |
|---|---|
ORIGIN_NOT_ALLOWED |
This domain is not among the key's allowed origins |
ORIGIN_REQUIRED |
A web key cannot be used without an Origin header |
DOMAIN_KEY_MISSING · DOMAIN_KEY_INVALID |
The key is missing, wrong or revoked |
DOMAIN_INACTIVE · TEAM_INACTIVE |
The domain or team is inactive |
MODULE_DISABLED |
Radio is not enabled in your package |
AUTO_CHANNEL_LIMIT |
The cap on automatically opened channels is reached; create the channel in the panel |
LIMIT_REACHED |
Your monthly record limit is full |
Chat and push are a separate surface
This page describes the Radio client in the browser (writing logs). Showing
live chat to a visitor and registering a push device is a separate surface and
uses a separate key (sb_public_live_…):
| What you want | Where to look |
|---|---|
| A ready-made chat bubble with no code | Widget |
| My own chat interface (React, Vue, Angular, plain JS) | App SDK |
| Chat in a mobile app | App SDK - React Native, Swift, Kotlin |
import { SignalbirdApp } from 'signalbird/app'
const chat = new SignalbirdApp({ publicKey: 'sb_public_live_…' })
await chat.startSession({ name: 'Ayşe' })
await chat.startConversation({ body: 'Where is my parcel?' })The two clients are independent of each other and work side by side on the same page.