Guides
Error Tracking
Catch errors before your users tweet about them
Error Tracking
Catch crashes before users report them. Get stack traces, breadcrumbs, and affected user counts.
Setup
<Analytics
apiKey="tif_xxx"
config={{
errors: {
enabled: true,
},
}}
/>That's it. Unhandled errors now get captured automatically.
Full Config
<Analytics
apiKey="tif_xxx"
config={{
errors: {
enabled: true,
captureConsoleErrors: false,
captureNetworkErrors: true,
maxBreadcrumbs: 25,
beforeSend: (payload) => payload,
},
}}
/>| Option | Default | Description |
|---|---|---|
enabled | true | Capture unhandled exceptions |
captureConsoleErrors | false | Treat console.error() as errors |
captureNetworkErrors | false | Catch failed fetch/XHR |
maxBreadcrumbs | 25 | Breadcrumbs to keep |
beforeSend | - | Filter/modify errors, return null to drop |
Automatic Capture
| Error Type | Captured |
|---|---|
| Uncaught exceptions | Yes |
| Unhandled promise rejections | Yes |
window.onerror events | Yes |
console.error() | If captureConsoleErrors: true |
| Failed fetch/XHR | If captureNetworkErrors: true |
Manual Capture
import { useCaptureException } from '@thisbefine/analytics/next';
const MyComponent = () => {
const captureException = useCaptureException();
const handleSubmit = async (data) => {
try {
await submitForm(data);
} catch (error) {
captureException(error as Error, { component: 'ContactForm' });
toast.error('Something went wrong.');
}
};
return <form>{/* form */}</form>;
};try {
await riskyOperation();
} catch (error) {
getAnalytics()?.captureException(error as Error, { operation: 'riskyOperation' });
}Capture Messages
Report errors without an Error object:
getAnalytics()?.captureMessage('API rate limit approaching', 'warning', { usage: 950, limit: 1000 });
getAnalytics()?.captureMessage('Payment failed', 'error', { orderId: 'ord_123' });
getAnalytics()?.captureMessage('Database connection lost', 'fatal', { attempts: 5 });Breadcrumbs
Trail of events leading up to an error.
Automatic Breadcrumbs
| Type | Captured |
|---|---|
click | User clicks |
navigation | Page changes |
console | console.error (if enabled) |
network | Failed requests (if enabled) |
Manual Breadcrumbs
getAnalytics()?.addBreadcrumb({
type: 'custom',
message: 'User started checkout',
data: { cartItems: 3, cartTotal: 149.97 },
});Fingerprinting
Similar errors are grouped by fingerprint (message + top stack frame). Same fingerprint = same group.
Filtering
Filter noise with beforeSend:
<Analytics
apiKey="tif_xxx"
config={{
errors: {
enabled: true,
beforeSend: (payload) => {
if (payload.stack?.includes('chrome-extension://')) return null;
if (payload.message.includes('ResizeObserver loop')) return null;
if (payload.stack?.includes('gtag')) return null;
return payload;
},
},
}}
/>You can also enrich errors:
beforeSend: (payload) => {
payload.context = { ...payload.context, buildId: process.env.NEXT_PUBLIC_BUILD_ID };
payload.message = payload.message.replace(/email=\S+/g, 'email=[REDACTED]');
return payload;
}React Error Boundaries
'use client';
import { Component, ReactNode } from 'react';
import { getAnalytics } from '@thisbefine/analytics';
interface Props { children: ReactNode; fallback: ReactNode; }
interface State { hasError: boolean; }
export class ErrorBoundary extends Component<Props, State> {
state = { hasError: false };
static getDerivedStateFromError() {
return { hasError: true };
}
componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
getAnalytics()?.captureException(error, { componentStack: errorInfo.componentStack });
}
render() {
return this.state.hasError ? this.props.fallback : this.props.children;
}
}<ErrorBoundary fallback={<div>Something broke.</div>}>
<MyComponent />
</ErrorBoundary>Best Practices
Don't log sensitive data: Never include passwords or tokens in error context.
Add useful context: Include component name, user action, relevant IDs.
Use breadcrumbs for critical flows: Add breadcrumbs before/after important operations so you can trace what happened.