Framework guide
Error tracking for React apps
@belvador/react reports render errors with the component stack, React 19 root errors and anything uncaught in the browser. It wraps Sentry's JavaScript engine 10.76.2, scrubs before sending, and the events are stored on your own node.
The short answer#
Install
@belvador/react, callinitwith your DSN and wrap the app inErrorBoundary.It reports render errors with the component stack, React 19 root errors and uncaught browser errors.
Events are scrubbed in the browser and again on your node in your Hetzner project.
Coming from
@sentry/react, you change the import and the DSN.
What it captures#
Render errors through
ErrorBoundary, with the component stack, while your fallback replaces the blank page.React 19 root errors —
onUncaughtError,onCaughtErrorandonRecoverableErrorviareactErrorHandler(). React 19 handles many errors itself, so without these hooks they never reachwindow.onerror.Uncaught exceptions and unhandled promise rejections once
inithas run.Stack traces mapped to your source when you upload source maps from CI (source maps).
What is not sent by default: breadcrumbs, attachments and trace headers are opt-in, and there is no replay or browser tracing (capabilities).
Install#
npm install @belvador/react@0.6.1Initialize (React 18 and 19)#
import { init } from "@belvador/react";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,});Import that file on the first line of src/main.tsx, before App, so a module that fails while loading is still reported:
import "./instrument"; // first line: starts Belvador before App's modules loadimport { createRoot } from "react-dom/client";import { ErrorBoundary } from "@belvador/react";import App from "./App";const root = document.getElementById("root");if (!root) throw new Error("Root element missing");createRoot(root).render( <ErrorBoundary fallback={<p>Something went wrong.</p>}> <App /> </ErrorBoundary>,);On React 19, also pass reactErrorHandler() to the root options; the React guide shows both versions and the Content Security Policy line to add.
Check it works#
Call verifyIntegration() once from production, or throw a tagged test error, and the project's Connection tab shows Verified for the browser (verify a connection).
Limits#
Browser only. For server rendering on Workers use TanStack Start; for a Node.js server use Node.js.
Next.js is not covered by a tested Belvador package yet.
No session replay and no user feedback dialog: the dialog loads a script from the DSN host, which Belvador never does.
Questions#
I already use @sentry/react. What changes?#
Replace the import with @belvador/react and the DSN with your Belvador DSN. captureException, withScope, setTag and ErrorBoundary keep their names (migrate from Sentry).
How big is it?#
In the 0.4.0 release build, @belvador/react with init and ErrorBoundary measured 37.6 KB gzip; @sentry/browser with its default init measured 28.7 KB in the same build. The SDK reference lists the current packages.
Posts on this topic
All posts- Production source maps with Vite: what each setting really shipsVite sourcemap false, true and hidden compared on one build: what ships, what a stack frame shows, and how to get readable stacks without public maps.
- Unhandled promise rejections vs caught errors: what your tracker receives14 browser failures run through an error tracker: which arrive, which vanish when caught and logged, and why rejecting with a string loses the stack.
- React error boundaries and async errors: what our lab caughtWhich errors a React 19 error boundary catches, which reach onCaughtError, onUncaughtError or window, and why standalone startTransition differed.
- JavaScript error handling for AI-generated apps: where errors escapeWhere JavaScript errors escape in React and Node.js: a lab with 13 failure cases, three runs each, and the few lines that make each one visible.
Try it on your own Hetzner project
Free during the beta. Hetzner bills the server; Belvador never bills per event.