@esportscz/sentry-react
v0.6.0
Published
Opinionated GlitchTip/Sentry bootstrap for Vite React SPAs
Readme
@esportscz/sentry-react
Opinionated GlitchTip/Sentry bootstrap for Vite React SPAs. Provides company-wide defaults, standard tags, and a minimal API so every project initializes Sentry the same way.
Installation
pnpm add @esportscz/sentry-reactQuick start
// src/sentry.ts
import { initSentry } from '@esportscz/sentry-react'
initSentry({
viteEnv: import.meta.env,
service: 'my-app',
project: 'my-project',
})// src/main.tsx
import { SentryErrorBoundary } from '@esportscz/sentry-react'
import './sentry'
import App from './App'
createRoot(document.getElementById('root')!).render(
<SentryErrorBoundary fallback={<p>Something went wrong.</p>}>
<App />
</SentryErrorBoundary>,
)// anywhere in app code
import { captureError } from '@esportscz/sentry-react'
try {
await saveOrder(payload)
} catch (error) {
captureError(error, {
feature: 'checkout',
action: 'save-order',
level: 'warning',
tags: { section: 'payment' },
extra: { orderId: payload.id },
})
}// React Router errorElement
import { useEffect } from 'react'
import { useRouteError } from 'react-router-dom'
import { captureRouteError } from '@esportscz/sentry-react'
export function ErrorPage() {
const error = useRouteError()
useEffect(() => {
captureRouteError(error, {
path: window.location.pathname,
source: 'render',
})
}, [error])
return <p>Something went wrong.</p>
}Environment variables
All values can be passed explicitly via SentryConfig or resolved automatically from Vite env variables. Explicit config always takes priority.
| Variable | Purpose | Fallbacks |
| --------------------------- | ------------------------ | -------------------------------------- |
| VITE_SENTRY_DSN | GlitchTip/Sentry DSN | |
| VITE_SENTRY_ENABLED | Enable/disable reporting | Enabled when DSN is present |
| VITE_SENTRY_ENVIRONMENT | Environment name | VITE_APP_ENV, MODE, "production" |
| VITE_SENTRY_RELEASE | Release/version string | VITE_APP_VERSION |
| VITE_GIT_COMMIT | Git commit SHA | VITE_BITBUCKET_COMMIT |
Config reference
interface SentryConfig {
dsn?: string // GlitchTip/Sentry DSN
enabled?: boolean // Kill switch (default: true when DSN present)
environment?: string // Environment name (default: "production")
release?: string // Release version
service?: string // Service tag for identifying the app
project?: string // Project tag for company grouping
stack?: string // Stack tag (default: "react")
gitCommit?: string // Git commit SHA tag
logPageContext?: boolean // Stamp router.path tag and contexts.page (default: true)
sampleRate?: number // Error sample rate, 0–1 (default: 1)
tracesSampleRate?: number // Performance trace sample rate, 0–1; enables tracing when set
sendDefaultPii?: boolean // Include default PII (default: false)
tanstackRouter?: unknown // TanStack Router instance for automatic tracing
dropEnvironments?: string[] // Environments where init is skipped
ignoreErrors?: Array<string | RegExp> // Error messages to ignore
denyUrls?: Array<string | RegExp> // Script URLs to ignore
allowUrls?: Array<string | RegExp> // Script URLs to allow
beforeSend?: BrowserOptions['beforeSend'] // Custom event hook
viteEnv?: Record<string, unknown> // import.meta.env for automatic resolution
}Production-safe defaults
When no explicit value is provided, the wrapper applies these defaults:
environment→"production"sampleRate→1(capture all errors)sendDefaultPii→false(no personal data)stack→"react"
Performance tracing is off by default. Set tracesSampleRate to enable browser tracing and control the sample rate.
TanStack Router tracing
For TanStack Router apps, install @tanstack/react-router version 1.64.0 or later. Create the router first, initialize Sentry with it, and mount the router afterwards. This uses Sentry's router-aware tracing integration; do not add manual router subscriptions or navigation spans.
import { createRoot } from 'react-dom/client'
import { RouterProvider } from '@tanstack/react-router'
import { initSentry } from '@esportscz/sentry-react'
import { router } from './router'
initSentry({
viteEnv: import.meta.env,
project: 'my-project',
tanstackRouter: router,
tracesSampleRate: 0.1,
})
createRoot(document.getElementById('root')!).render(<RouterProvider router={router} />)Standard tags
Every initialized project gets these tags on all events:
| Tag | Source |
| ------------- | ------------------------------------------------- |
| service | config.service |
| project | config.project |
| stack | config.stack or "react" |
| environment | Resolved environment |
| release | Resolved release |
| git.commit | config.gitCommit or VITE_GIT_COMMIT / VITE_BITBUCKET_COMMIT |
Per-event location data
Location data is split between tags (for filtering) and contexts (for event details):
| Data | Tag | Context |
| ---- | --- | ------- |
| Pathname / route path | router.path | contexts.page.path or contexts.route.path |
| Sanitized page URL | — | contexts.page.url (no query string or hash) |
| Route id, source | — | contexts.route.route_id, contexts.route.source |
| Route params, status | — | extra data (router.params, etc.) |
Every event gets a fresh contexts.page object at send time and a router.path tag from the current pathname — no manual sync needed after SPA navigation.
When captureRouteError is used, route metadata is attached as both tags and contexts.route. The router.path tag uses the route pattern from context (e.g. /orders/:id) and is not overwritten by the browser pathname.
Set logPageContext: false to disable automatic router.path tagging and contexts.page stamping.
API
initSentry(config?): boolean
Initializes Sentry with company defaults. Returns true if initialized, false if skipped (missing DSN, disabled, or dropped environment). Safe to call multiple times — subsequent calls return true without re-initializing.
isInitialized(): boolean
Returns whether Sentry has been initialized.
setTag(key, value): void
Sets a custom tag. If called before initSentry, the tag is stored and forwarded once Sentry initializes. Empty, null, or undefined values are ignored.
getTags(): Record<string, string | number | boolean>
Returns a copy of all tracked tags.
clearTag(key): void
Removes a tracked tag. If called after initSentry, also clears it on the Sentry scope.
setUser(user): void
Sets the Sentry user context. Accepts a SentryUser object or null to clear. If called before initSentry, the user is stored and forwarded once Sentry initializes.
getUser(): SentryUser | null
Returns a copy of the current user context.
SentryErrorBoundary
Re-export of @sentry/react's ErrorBoundary component. Use it to wrap your app and capture React rendering errors.
captureError(error, context?): string | undefined
Use this when your app catches an error anywhere outside React rendering and still wants to report it.
type SentryTagValue = string | number | boolean
type CaptureErrorLevel = 'error' | 'warning' | 'fatal'
interface CaptureErrorContext {
feature?: string
action?: string
level?: CaptureErrorLevel
tags?: Record<string, SentryTagValue | null | undefined>
extra?: Record<string, unknown>
}Behavior:
- Accepts
unknownand normalizes non-Errorvalues before capture - Applies
feature,action, andtagsas event-scoped tags - Applies
extraas event-scoped extra data - Returns the Sentry event id when available
- Safely returns
undefinedwhen Sentry has not been initialized or was intentionally skipped
captureRouteError(error, context?): string | undefined
Use this inside React Router errorElement flows and pass it whatever useRouteError() returned.
interface CaptureRouteErrorContext {
path?: string
source?: 'loader' | 'action' | 'render'
routeId?: string
params?: Record<string, string | undefined>
captureErrorResponses?: 'server-errors' | 'all' | 'none'
}Default route-error policy:
- Thrown
Errorvalues are reported - Unknown thrown values are normalized and reported
ErrorResponsevalues withstatus >= 500are reportedErrorResponsevalues withstatus 400-499, especially404, are skipped by default- Set
captureErrorResponses: 'all'to report allErrorResponsevalues - Set
captureErrorResponses: 'none'to skip allErrorResponsevalues
Router metadata is attached under contexts.route, with only router.path as a tag for filtering:
router.path/contexts.route.path— route pattern or path fromcaptureRouteErrorcontext (e.g./orders/:id)contexts.route.route_id,contexts.route.source— detail metadata, context only
Route params are attached as extra data under router.params, not as tags.
Recommended integration patterns
Use the pattern that matches how the SPA handles errors.
1. React Router data router apps
Use captureRouteError(...) inside route errorElement components for loader/action/render failures in router flows.
// routes/ErrorPage.tsx
import { useEffect } from 'react'
import { useRouteError } from 'react-router-dom'
import { captureRouteError } from '@esportscz/sentry-react'
export function ErrorPage() {
const error = useRouteError()
useEffect(() => {
captureRouteError(error, {
path: '/orders/:id',
routeId: 'order-details',
source: 'loader',
})
}, [error])
return <p>Something went wrong.</p>
}Use captureError(...) for caught errors in app code. Add SentryErrorBoundary only if you also want a general React error boundary outside router-managed error flows.
2. Plain BrowserRouter apps with Route components
If the app does not use loader/action errorElement flows, wrap the app in SentryErrorBoundary and use captureError(...) for caught async or business-logic errors.
// src/main.tsx
import { BrowserRouter } from 'react-router-dom'
import { SentryErrorBoundary } from '@esportscz/sentry-react'
import './sentry'
import App from './App'
createRoot(document.getElementById('root')!).render(
<SentryErrorBoundary fallback={<p>Something went wrong.</p>}>
<BrowserRouter>
<App />
</BrowserRouter>
</SentryErrorBoundary>,
)This setup captures React render crashes through the boundary and still gives every outgoing event a fresh router.path tag and contexts.page data.
Disabling Sentry
Three ways to prevent initialization:
- Don't set
VITE_SENTRY_DSN—initSentryreturnsfalseand does nothing - Set
enabled: false(orVITE_SENTRY_ENABLED=false) - Use
dropEnvironmentsto skip specific environments:initSentry({ viteEnv: import.meta.env, service: 'my-app', dropEnvironments: ['development', 'test'], })
