Next.js SDK
Error tracking, crash detection, breadcrumbs, and React integration for Next.js apps using App Router or Pages Router.
Automatic capture of uncaught errors, promise rejections, and React errors
Browser tab crash and unresponsiveness detection
Track user actions and events for error context
Automatic source map support for readable stack traces
Built for App Router and Pages Router
Error boundaries, hooks, and components
Works in browser and Node.js (API routes, server components)
Navigation tracking, user sessions, custom metadata
Sampling, filtering, and hooks
Installation#
Packages come from Abrova Center. Two steps:
1. Add the Abrova registry to .npmrc in your project root (npm, yarn and pnpm all read it):
# .npmrc @abrova-trace:registry=https://center.abrova.ir/api/packages/abrova/npm/
2. Install the package:
npm install @abrova-trace/nextjs-sdk # or yarn add @abrova-trace/nextjs-sdk # or pnpm add @abrova-trace/nextjs-sdk
Quick Start#
App Router (Next.js 13+)
Create app/providers.tsx:
'use client';
import { useEffect } from 'react';
import { AbrovaTrace } from '@abrova-trace/nextjs-sdk';
import { useAbrovaTraceNavigation } from '@abrova-trace/nextjs-sdk/react';
export function Providers({ children }: { children: React.ReactNode }) {
useEffect(() => {
AbrovaTrace.init({
apiKey: 'ab_live_your_api_key',
environment: process.env.NODE_ENV,
release: process.env.NEXT_PUBLIC_APP_VERSION,
debug: process.env.NODE_ENV === 'development',
});
}, []);
// Track navigation
useAbrovaTraceNavigation();
return <>{children}</>;
}useAbrovaTraceNavigation() reads useSearchParams(). If next build complains about a missing Suspense boundary, call it from a small component wrapped in <Suspense> instead (see the NavigationTracker in Navigation Tracking below).
Update app/layout.tsx:
import { Providers } from './providers';
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
<Providers>{children}</Providers>
</body>
</html>
);
}Pages Router (Next.js 12 and earlier)
Update pages/_app.tsx:
import { useEffect } from 'react';
import type { AppProps } from 'next/app';
import { AbrovaTrace } from '@abrova-trace/nextjs-sdk';
import { useAbrovaTracePagesRouter } from '@abrova-trace/nextjs-sdk/react';
function MyApp({ Component, pageProps }: AppProps) {
useEffect(() => {
AbrovaTrace.init({
apiKey: 'ab_live_your_api_key',
environment: process.env.NODE_ENV,
release: process.env.NEXT_PUBLIC_APP_VERSION,
});
}, []);
// Track navigation
useAbrovaTracePagesRouter();
return <Component {...pageProps} />;
}
export default MyApp;Error Boundary#
Wrap your components with the error boundary to automatically catch and report React errors:
Basic Usage
import { ErrorBoundary } from '@abrova-trace/nextjs-sdk/react';
export default function Page() {
return (
<ErrorBoundary fallback={<div>Something went wrong!</div>}>
<YourComponent />
</ErrorBoundary>
);
}With Reset Function
<ErrorBoundary
fallback={(error, errorInfo, resetError) => (
<div>
<h1>Error: {error.message}</h1>
<button onClick={resetError}>Try again</button>
</div>
)}
>
<YourComponent />
</ErrorBoundary>With Error Handler
<ErrorBoundary
onError={(error, errorInfo) => {
console.log('Error occurred:', error);
// Custom error handling
}}
>
<YourComponent />
</ErrorBoundary>Default Fallback
Without a fallback, the boundary renders a built-in panel: a "Something went wrong" heading, a short message, and a Try again button that resets the boundary. Since 0.1.8 the error message, stack trace and component stack are no longer shown in production: they appear in a collapsible "Error details" block only when showErrorDetails is true, and it defaults to process.env.NODE_ENV !== 'production' (shown in development, hidden in production builds). A custom fallback is rendered as you wrote it and is not affected by showErrorDetails.
<ErrorBoundary
fallbackMessage="We could not load this section."
showErrorDetails={false}
>
<YourComponent />
</ErrorBoundary>Props
| Option | Type | Required | Description |
|---|---|---|---|
| children | ReactNode | Yes | The components to protect |
| fallback | ReactNode | function | No | Custom UI shown after an error: an element, or a function (error, errorInfo, resetError) that returns one. Replaces the default fallback |
| onError | function | No | Called with (error, errorInfo) after the error has been reported |
| showDefaultFallback | boolean | No | Render the built-in fallback when no fallback is given (default: true). With false, nothing is rendered |
| fallbackMessage | string | No | Message in the built-in fallback (default: "An unexpected error occurred. Our team has been notified.") |
| showErrorDetails | boolean | No | Show the error message and stack trace in the built-in fallback (default: true in development, false in production) |
Manual Error Capture#
Using Hooks
import { useAbrovaTrace } from '@abrova-trace/nextjs-sdk/react';
function MyComponent() {
const { captureException, captureMessage, setUserId } = useAbrovaTrace();
const handleClick = async () => {
try {
await riskyOperation();
} catch (error) {
captureException(error as Error, 'Failed to complete operation', {
userId: user.id,
action: 'button_click',
});
}
};
return <button onClick={handleClick}>Click me</button>;
}Direct API Usage
import { AbrovaTrace } from '@abrova-trace/nextjs-sdk';
// Capture exception
try {
throw new Error('Something broke');
} catch (error) {
AbrovaTrace.getInstance()?.captureException(error as Error, 'Custom context', {
userId: '123',
customData: 'value',
});
}
// Capture a message (level: 'debug' | 'info' | 'warning' | 'error')
AbrovaTrace.getInstance()?.captureMessage(
'User completed checkout',
'info',
{ orderId: '456' }
);Messages appear in the Console under Abrova Trace → Messages, with their level, platform, extra data and breadcrumbs. They never create an issue. Levels: 'debug', 'info' (default), 'warning', 'error'. Use messages for things your app reports on purpose ("checkout started", "sync finished"); for high-volume diagnostics use Logs.
Breadcrumbs#
Breadcrumbs track user actions and provide context for errors. Navigation changes (once you use a navigation hook) and console.error / console.warn calls are recorded automatically.
Manual Breadcrumbs
import { BreadcrumbService, BreadcrumbType } from '@abrova-trace/nextjs-sdk';
// Generic breadcrumb
BreadcrumbService.add('User action', BreadcrumbType.User, { button: 'submit' });
// Specific types
BreadcrumbService.addUser('Clicked checkout button');
BreadcrumbService.addHttp('POST', '/api/orders', 200);
BreadcrumbService.addState('Cart updated', { items: 3 });
BreadcrumbService.addNavigation('/cart', '/checkout');Using Hooks
import { useBreadcrumbs } from '@abrova-trace/nextjs-sdk/react';
function Component() {
const { addBreadcrumb, addHttp, addState } = useBreadcrumbs();
const handleSubmit = async (data) => {
addBreadcrumb('Form submitted', { formName: 'contact' });
const response = await fetch('/api/contact', {
method: 'POST',
body: JSON.stringify(data),
});
addHttp('POST', '/api/contact', response.status);
};
return <form onSubmit={handleSubmit}>...</form>;
}Crash Detection#
The SDK detects browser tab crashes and unresponsiveness using a localStorage-based heartbeat system.
- Tab Crashes — Detects browser tab crashes
- Unresponsive Tabs — Detects frozen/hung tabs
- Force-Close — Detects force-close scenarios
- Post-Mortem — Reporting on next launch
How It Works
- App starts — Crash marker created in localStorage
- Heartbeat runs every 5 seconds
- App closes normally — Marker removed
- App crashes — Marker remains
- Next session — Crash detected and reported
API Routes & Server Components#
The init() call in Providers / _app.tsx runs in the browser only, so on the server AbrovaTrace.getInstance() returns null until you initialize the SDK there too. Create a small server helper (init() returns the existing instance if one is already set up):
// lib/abrova-trace-server.ts
import { AbrovaTrace } from '@abrova-trace/nextjs-sdk';
export const trace =
AbrovaTrace.getInstance() ??
AbrovaTrace.init({
apiKey: process.env.ABROVA_TRACE_API_KEY!,
environment: process.env.NODE_ENV,
});API Routes
// pages/api/example.ts
import type { NextApiRequest, NextApiResponse } from 'next';
import { trace } from '@/lib/abrova-trace-server';
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
try {
// Your API logic
} catch (error) {
trace.captureException(error as Error, 'API Error', {
endpoint: '/api/example',
method: req.method,
});
res.status(500).json({ error: 'Internal Server Error' });
}
}Server Components (App Router)
// app/page.tsx
import { trace } from '@/lib/abrova-trace-server';
export default async function Page() {
try {
const data = await fetchData();
return <div>{data}</div>;
} catch (error) {
trace.captureException(error as Error, 'Server Component Error');
return <div>Error loading data</div>;
}
}Source Maps#
Enable production source maps for readable stack traces in your next.config.js:
// next.config.js
module.exports = {
productionBrowserSourceMaps: true,
};After building your app, upload the .map files with the Abrova Trace CLI, using the same release you pass to AbrovaTrace.init():
npx @abrova-trace/cli sourcemaps upload .next/static --release 1.0.0 --project YOUR_PROJECT_ID --token ab_pat_xxx
Configuration Options#
AbrovaTrace.init({
apiKey: 'ab_live_your_api_key', // Required
environment: 'production', // Default: 'production'
release: '1.0.0', // Optional
captureUncaughtErrors: true, // Default: true
enabled: true, // Default: true
debug: false, // Default: false
timeout: 10, // Default: 10 seconds
enableServerSide: true, // Default: true
enableClientSide: true, // Default: true
enableCrashDetection: true, // Default: true
maxBreadcrumbs: 20, // Default: 20
sampleRate: 1.0, // 0.0 to 1.0
tags: { team: 'frontend' }, // Custom tags
ignoreErrors: ['ResizeObserver loop limit exceeded'],
ignoreUrls: [/chrome-extension:\/\//], // matched against the stack trace
beforeSend: (error) => error, // Filter/modify errors
});| Option | Type | Required | Description |
|---|---|---|---|
| apiKey | string | Yes | Your project API Key |
| environment | string | No | Environment name (default: "production") |
| release | string | No | App version identifier |
| captureUncaughtErrors | boolean | No | Catch uncaught errors automatically (default: true) |
| enabled | boolean | No | Enable/disable SDK (default: true) |
| debug | boolean | No | Enable debug logging (default: false) |
| timeout | number | No | HTTP request timeout in seconds (default: 10) |
| enableServerSide | boolean | No | Capture uncaught errors and unhandled rejections automatically on the server (default: true) |
| enableClientSide | boolean | No | Capture uncaught errors and unhandled rejections automatically in the browser (default: true) |
| enableCrashDetection | boolean | No | Enable crash detection (default: true) |
| maxBreadcrumbs | number | No | Max breadcrumbs to keep (default: 20) |
| sampleRate | number | No | Sample rate for errors and messages, 0.0-1.0 (default: 1.0) |
| tags | object | No | Custom tags to attach to all errors |
| ignoreErrors | array | No | Error messages to ignore (strings or RegExp) |
| ignoreUrls | array | No | Strings or RegExp matched against the error stack trace; a match drops the error |
| beforeSend | function | No | Hook to modify/filter errors before sending; return null or false to drop the error |
| apiUrl | string | No | Base URL of the Trace API (default: "https://trace.abrova.ir"). Set it only for self-hosted |
User Identification#
import { AbrovaTrace } from '@abrova-trace/nextjs-sdk';
// Set user ID after authentication
AbrovaTrace.getInstance()?.setUserId('user-123');
// Get user ID
const userId = AbrovaTrace.getInstance()?.getUserId();Navigation Tracking#
App Router (Next.js 13+)
'use client';
import { useAbrovaTraceNavigation } from '@abrova-trace/nextjs-sdk/react';
export function NavigationTracker() {
useAbrovaTraceNavigation();
return null;
}Pages Router
import { useAbrovaTracePagesRouter } from '@abrova-trace/nextjs-sdk/react';
function MyApp({ Component, pageProps }) {
useAbrovaTracePagesRouter();
return <Component {...pageProps} />;
}Manual Tracking
import { RouterIntegration } from '@abrova-trace/nextjs-sdk';
RouterIntegration.setCurrentRoute('/custom-route');Context & Tags#
Attach persistent metadata to every error and message. Tags are categorical filters; extras are richer per-event payloads. (Logs, spans and Web Vitals do not carry them.)
Global extras
const sdk = AbrovaTrace.getInstance();
sdk?.setExtra('organizationId', 'org-123');
sdk?.setExtra('feature', 'checkout-v2');
sdk?.setExtras({
organizationId: 'org-123',
feature: 'checkout-v2',
experiment: 'new-flow',
});
const orgId = sdk?.getExtra('organizationId');
const allExtras = sdk?.getExtras();
sdk?.clearExtras();Tags
const sdk = AbrovaTrace.getInstance();
sdk?.setTag('team', 'payments');
sdk?.setTag('region', 'us-east');
sdk?.setTags({ team: 'payments', version: '2.1' });
const team = sdk?.getTag('team');
const allTags = sdk?.getTags();
sdk?.clearTags();Session ID
A unique session ID is auto-generated when the SDK initializes (16 random hex bytes) and attached to every error and message for session grouping in Abrova Console:
const sessionId = AbrovaTrace.getInstance()?.getSessionId();
Web Vitals#
Track Core Web Vitals using Google's official web-vitals library (browser only). Metrics show up in Abrova Console under Abrova Trace → Performance alongside HTTP spans.
const sdk = AbrovaTrace.getInstance();
sdk?.enableWebVitals({
reportAllChanges: false, // Only report final values (default)
batchInterval: 5000, // Send 5 s after the last metric (default); also sent when the page is hidden
});Tracked metrics:
| Option | Type | Required | Description |
|---|---|---|---|
| LCP | - | - | Largest Contentful Paint |
| CLS | - | - | Cumulative Layout Shift |
| INP | - | - | Interaction to Next Paint |
| TTFB | - | - | Time to First Byte |
| FCP | - | - | First Contentful Paint |
Web Vitals events automatically include device type (mobile/tablet/desktop), connection type, page URL, and route information.
TypeScript#
The SDK is written in TypeScript and ships with full type definitions. Import the types when you need explicit annotations on config objects or event payloads:
import type {
AbrovaTraceConfig,
AbrovaTraceError,
Breadcrumb,
} from '@abrova-trace/nextjs-sdk';
import type {
PerformanceSpan,
WebVitalsData,
LogEntry,
LogLevel,
LogServiceConfig,
} from '@abrova-trace/nextjs-sdk';Logging#
Structured server-side logging that works in App Router, Pages Router, and API Routes. Logging uses a dedicated LogService instance — create one in lib/logger.ts and import it from anywhere:
import { LogService } from '@abrova-trace/nextjs-sdk';
export const logger = new LogService({
apiKey: process.env.ABROVA_TRACE_API_KEY!,
sourceId: 'nextjs-app',
platform: 'nextjs',
environment: process.env.NODE_ENV,
});
logger.log('Users fetched', 'info', { count: 42 });You can also log through the SDK instance, which reuses the init() config: AbrovaTrace.getInstance()?.info('Users fetched', { count: 42 }) (see the Logging table in the API Reference below). Logs are separate from captureMessage(): they are batched and appear under Abrova Trace → Logs.
See Next.js SDK Logging for the full pipeline — App Router, Pages Router, API Routes, log levels, and request middleware.
API Reference#
AbrovaTrace — Initialization & lifecycle
| Option | Type | Required | Description |
|---|---|---|---|
| init(config) | - | - | Initialize SDK |
| getInstance() | - | - | Get the singleton SDK instance |
| close() | - | - | Cleanup SDK, flush pending data |
Error capture
| Option | Type | Required | Description |
|---|---|---|---|
| captureException(error, message?, extra?, level?, callback?) | - | - | Capture an error / exception |
| captureMessage(message, level?, extra?, callback?) | - | - | Send a message to Abrova Trace → Messages (not an issue) |
User & session
| Option | Type | Required | Description |
|---|---|---|---|
| setUserId(userId) | - | - | Set the current user identifier |
| getUserId() | - | - | Get the current user identifier |
| getSessionId() | - | - | Get the auto-generated session ID |
Global context (extras)
| Option | Type | Required | Description |
|---|---|---|---|
| setExtra(key, value) | - | - | Set a single extra |
| setExtras(extras) | - | - | Set multiple extras at once |
| getExtra(key) | - | - | Get a single extra value |
| getExtras() | - | - | Get all extras |
| clearExtras() | - | - | Clear all extras |
Tags
| Option | Type | Required | Description |
|---|---|---|---|
| setTag(key, value) | - | - | Set a single tag |
| setTags(tags) | - | - | Set multiple tags at once |
| getTag(key) | - | - | Get a single tag value |
| getTags() | - | - | Get all tags |
| clearTags() | - | - | Clear all tags |
Performance monitoring
| Option | Type | Required | Description |
|---|---|---|---|
| enablePerformanceTracking(options?) | - | - | Turn on performance reporting; in the browser this also tracks every fetch call. Options: batchSize (default: 5), flushInterval in ms (default: 5000) |
| enableWebVitals(options?) | - | - | Turn on Web Vitals tracking |
| reportPerformanceSpan(span) | - | - | Report a single span |
| reportPerformanceSpanBatch(spans) | - | - | Report multiple spans at once |
| trackOperation(operation, fn, options?) | - | - | Time and report an async operation |
| createPerformanceFetch() | - | - | Build a fetch wrapper that reports a span per request. It reports only after enablePerformanceTracking(), trackOperation() or reportPerformanceSpan() has been called |
| flushPerformance() | - | - | Flush buffered performance data immediately |
Logging
| Option | Type | Required | Description |
|---|---|---|---|
| enableLogging(options?) | - | - | Turn on the log pipeline |
| log(message, level?, metadata?) | - | - | Log at an explicit level |
| trace(message, metadata?) | - | - | Log at trace level |
| logDebug(message, metadata?) | - | - | Log at debug level |
| info(message, metadata?) | - | - | Log at info level |
| warn(message, metadata?) | - | - | Log at warn level |
| logError(message, metadata?) | - | - | Log at error level |
| fatal(message, metadata?) | - | - | Log at fatal level |
| flushLogs() | - | - | Flush buffered logs immediately |
| pendingLogCount | - | - | Number of logs still in the buffer |
BreadcrumbService
| Option | Type | Required | Description |
|---|---|---|---|
| add(message, type, data?, level?) | - | - | Add a generic breadcrumb |
| addNavigation(from, to, data?) | - | - | Add a navigation breadcrumb |
| addUser(action, data?) | - | - | Add a user-action breadcrumb |
| addHttp(method, url, status?, data?) | - | - | Add an HTTP breadcrumb |
| addState(message, data?) | - | - | Add a state-change breadcrumb |
| getAll() | - | - | Get all breadcrumbs |
| clear() | - | - | Clear all breadcrumbs |
RouterIntegration
| Option | Type | Required | Description |
|---|---|---|---|
| trackAppRouterNavigation(pathname, searchParams?) | - | - | Track App Router navigation manually |
| initializePagesRouter(router) | - | - | Initialize Pages Router tracking |
| setCurrentRoute(route) | - | - | Override the current route |
| getCurrentRoute() | - | - | Get the current route |
| getNavigationContext() | - | - | Get the active navigation context |
CrashDetector
| Option | Type | Required | Description |
|---|---|---|---|
| enable() | - | - | Turn on crash detection |
| disable() | - | - | Turn off crash detection |
| didCrashLastSession() | - | - | True if the previous session ended in a crash |
| getCrashReport() | - | - | Get details of the previous crash |
React hooks
| Option | Type | Required | Description |
|---|---|---|---|
| useAbrovaTrace() | - | - | Returns { captureException, captureMessage, setUserId, setExtra, setExtras, setTag, setTags } |
| useAbrovaTraceNavigation() | - | - | Track App Router navigation automatically |
| useAbrovaTracePagesRouter() | - | - | Track Pages Router navigation automatically |
| useBreadcrumbs() | - | - | Returns { addBreadcrumb, addNavigation, addHttp, addState } |
React components
| Option | Type | Required | Description |
|---|---|---|---|
| ErrorBoundary | - | - | React error boundary with automatic error reporting (alias: AbrovaTraceErrorBoundary) |
Best Practices#
Initialize AbrovaTrace as early as possible in your app lifecycle to catch all errors
Set user ID after authentication to associate errors with users
Wrap major sections of your app with error boundaries for automatic React error capture
Include user IDs, amounts, and relevant data when capturing errors
Add breadcrumbs for important user actions to provide error context
Use different configs for production vs development (debug mode, sample rate)
Call AbrovaTrace.getInstance()?.close() when cleaning up for proper crash detection