# JavaScript in the browser

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

Use `@belvador/browser` for browser JavaScript that does not use React — plain TypeScript, a site built with Vite, a widget. For React use [React](https://belvador.dev/en/docs/sdk/react); for Lovable or TanStack Start use [TanStack Start](https://belvador.dev/en/docs/sdk/tanstack-start).

## Before you start

- A project with platform **JavaScript (browser)** and its DSN — see [Projects and DSN](https://belvador.dev/en/docs/projects-and-dsn).
- A bundler that handles ES modules (Vite, esbuild, Rollup, webpack 5).

## 1. Install

```bash
npm install @belvador/browser@0.6.1
```

## 2. Initialize

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

```ts title="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.

```ts title="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](https://belvador.dev/en/docs/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](https://belvador.dev/en/docs/sdk/configuration#breadcrumbs)).

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:

```ts
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

```ts
import { setContext, setTag, setUser } from "@belvador/browser";

setUser({ id: account.id }); // only the id is kept
setTag("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](https://belvador.dev/en/docs/privacy).

## Check that it works

1. Deploy or run the app with the setup above.
2. Trigger a test error, for example from a button:

```ts
setTimeout(() => {
  throw new Error("Belvador browser test");
}, 0);
```

3. 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](https://belvador.dev/en/docs/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](https://belvador.dev/en/docs/source-maps) to show your original file and line.
