Middle-Monitor Web SDK
Web SDK for Middle-Monitor: error reporting and fetch/XHR tracing for any frontend framework.
Documentation: middlemonitor.io/docs#sdk-web
Framework-agnostic by design. The core hooks into the browser itself, so React, Vue, Angular, Svelte or plain JS all use the same three lines. Frameworks only differ in where they swallow errors, which is one extra line each (see Framework integration).
For a Node backend (Express, etc.), use @middle-monitor/sdk instead. This package will not run on the server.
Installation
npm install @middle-monitor/web
Usage
import { init } from '@middle-monitor/web';
init({
apiUrl: 'https://api.middlemonitor.io',
service: 'shop-web',
token: import.meta.env.VITE_MIDDLE_MONITOR_TOKEN,
});
That is the whole setup. From there the SDK reports uncaught errors and unhandled promise rejections to the Errors view, and traces fetch / XMLHttpRequest calls.
Call it once, as early as possible — before your app mounts, so a crash during the first render is still caught.
Which token
Use a service token (Services > your service > token in the dashboard). A browser bundle is public: whatever token you put here is readable by every visitor, so it must be one that can only ingest.
Never use an mm_ org API key. Those authenticate on the dashboard API and would give any visitor read and write access to your whole organization.
Linking frontend traces to your backend
By default the SDK only adds the W3C traceparent header to same-origin requests. A cross-origin request carrying an unexpected header fails the target's CORS preflight, so it would break your API calls rather than trace them.
If your API is on another origin, list it — and allow the header on your side:
init({
apiUrl: 'https://api.middlemonitor.io',
service: 'shop-web',
token: import.meta.env.VITE_MIDDLE_MONITOR_TOKEN,
propagateTraceHeaderCorsUrls: [/^https:\/\/api\.myshop\.com/],
});
Your API must answer with Access-Control-Allow-Headers: traceparent. Once it does, a browser span and the server span it triggered belong to the same trace: a failed request in the UI leads straight to the handler that failed.
Reporting an error yourself
import { captureError } from '@middle-monitor/web';
try {
await checkout(cart);
} catch (err) {
captureError(err);
showFallbackUI();
}
Framework integration
Every framework catches render errors before they reach window, so the global handler never sees them. Wire captureError into the framework's own hook to close that gap.
React
import { Component, ReactNode } from 'react';
import { captureError } from '@middle-monitor/web';
class ErrorBoundary extends Component<{ children: ReactNode }> {
componentDidCatch(error: Error, info: { componentStack: string }) {
captureError(error, { file: info.componentStack.trim().split('\n')[0] });
}
render() {
return this.props.children;
}
}
Vue
import { captureError } from '@middle-monitor/web';
app.config.errorHandler = (err, instance, info) => {
captureError(err, { file: info });
};
Angular
import { ErrorHandler, Injectable } from '@angular/core';
import { captureError } from '@middle-monitor/web';
@Injectable()
export class MiddleMonitorErrorHandler implements ErrorHandler {
handleError(error: unknown) {
captureError(error);
}
}
// providers: [{ provide: ErrorHandler, useClass: MiddleMonitorErrorHandler }]
Svelte
import { captureError } from '@middle-monitor/web';
// src/hooks.client.ts (SvelteKit)
export function handleError({ error }) {
captureError(error);
}
Options
| Option | Default | Description |
|---|---|---|
apiUrl |
https://api.middlemonitor.io |
Middle-Monitor ingest URL. |
service |
required | Service name shown in the dashboard. |
token |
none | Service token. Reports land in the wrong place without it. |
tracesSampleRate |
0.1 |
Share of fetch/XHR traces to keep, 0 to 1. -1 selects the default. Errors are never sampled out. |
propagateTraceHeaderCorsUrls |
[] |
Cross-origin URLs allowed to receive traceparent. |
ignoreUrls |
[] |
URLs to never trace. The Middle-Monitor endpoint is always ignored. |
captureGlobalErrors |
true |
Install window error and rejection handlers. |
timeout |
5000 |
Export timeout in milliseconds. |
Sampling
tracesSampleRate applies to fetch/XHR spans only. Reported errors always reach the Errors view, whatever the rate: a dropped crash report is a crash nobody sees.
Unlike the server SDKs, the browser cannot keep a trace because it failed. The sampling decision is made when a request starts, and the status only exists once it ends. Set the rate to 1 on a low-traffic frontend if you want every request.
API
init(options)— start the SDK. Idempotent: a second call is ignored, so HMR does not double-report.captureError(error, location?)— report an error. Accepts anything throwable, not justError.flush()— export pending spans now.shutdown()— detach handlers and stop exporting.