Skip to content

JavaScript in the browser

Install @belvador/browser for websites and browser apps that do not use React.

Markdown
On this page

Use @belvador/browser for browser JavaScript that does not use React — plain TypeScript, a site built with Vite, a widget. For React use React; for Lovable or TanStack Start use TanStack Start.

Before you start#

  • A project with platform JavaScript (browser) and its DSN — see Projects and DSN.

  • A bundler that handles ES modules (Vite, esbuild, Rollup, webpack 5).

1. Install#

Terminal
npm install @belvador/browser@0.6.1

2. Initialize#

Put init in its own file, src/instrument.ts:

src/instrument.ts
import { init } from "@belvador/browser";init({  dsn: "https://<public-key>@ingest.belvador.dev/<project-id>",  release: import.meta.env.VITE_APP_VERSION, // your build or commit id  environment: import.meta.env.MODE,});

Then make it the first import of your entry file. Imports run before the rest of a file, so an init call written below your other imports never runs when one of those modules fails while loading — a missing environment variable, for example — and the error is never reported.

src/main.ts
import "./instrument"; // first line: starts Belvador before your app's modules load// …the rest of your imports and start-up code
  • release is your app's version, such as a build number or commit id; it lets Belvador tell which deploy an error came from and match source maps. Set VITE_APP_VERSION in your build, or pass any string.

  • environment separates production from staging. It defaults to production when left out.

init returns true when error tracking is running. It never throws: an invalid DSN logs one warning in the console and leaves the SDK switched off. Calling init again with the same DSN does nothing.

3. Allow the ingest address in your CSP#

If your site sends a Content-Security-Policy header, add the ingest origin to connect-src. Merge it into your existing policy — do not replace the policy:

Text
connect-src 'self' https://ingest.belvador.dev;

Without it the browser blocks the request and errors never arrive.

What is captured#

As soon as init has run:

  • uncaught errors (window.onerror) and

  • unhandled promise rejections.

Errors from browser extensions are ignored. Breadcrumbs from the console, clicks, fetch, XHR and navigation are off by default; turn them on with the instrument option (SDK options).

Frameworks that catch errors themselves (for example Vue's app.config.errorHandler or Angular's ErrorHandler) keep those errors away from window.onerror. Call captureException from that hook to report them.

Report errors you handle#

When your code catches an error and recovers, the SDK does not see it. Report it yourself when you still want to know:

TypeScript
import { captureException } from "@belvador/browser";async function saveOrder(order: Order) {  try {    await api.post("/orders", order);  } catch (error) {    captureException(error, { tags: { feature: "checkout" } });    showToast("We couldn't save your order. Please try again.");  }}

captureException returns an event ID. The ID is created in your app; it does not prove the event reached your node.

Add context#

TypeScript
import { setContext, setTag, setUser } from "@belvador/browser";setUser({ id: account.id }); // only the id is keptsetTag("plan", account.plan);setContext("cart", { items: cart.length });

Do not put emails, names or tokens in tags or context: they are shown to everyone in the workspace. The SDK masks common secret patterns, but that is a safety net, not a filter you can rely on — see Data protection.

Check that it works#

  1. Deploy or run the app with the setup above.

  2. Trigger a test error, for example from a button:

TypeScript
setTimeout(() => {  throw new Error("Belvador browser test");}, 0);
  1. Open the project in Belvador: the issue appears within a few seconds. If not, open your browser's network tab and look for a POST to https://ingest.belvador.dev/api/…/envelope/ — Troubleshooting explains each status code.

Limits to know#

  • Errors from scripts on another domain show up as Script error. without details unless that script is served with CORS headers and loaded with crossorigin="anonymous" — a browser rule, not a Belvador one.

  • Events wait in memory only. If the page closes before they are sent, they are lost.

  • Stack traces of minified code need source maps to show your original file and line.

Posts on this topic

All posts

Spotted something wrong or missing? Tell us: hello@belvador.dev