JustAnalytics for Next.js
Step-by-step guide to add analytics, error tracking, and APM to your Next.js app
JustAnalytics for Next.js
Add full-stack observability to your Next.js application — distributed tracing, error tracking, structured logging, and infrastructure metrics.
Time to data: 3-5 minutesPrerequisites#
- Next.js 14+ (App Router recommended; Pages Router also supported)
- Node.js 18+
- A JustAnalytics account with a Site ID and API key
Install the SDK
npm install @justanalyticsapp/node
Or with your preferred package manager:
yarn add @justanalyticsapp/node
# or
pnpm add @justanalyticsapp/node
Initialize via instrumentation.ts
Next.js 15+ provides an instrumentation.ts hook that runs once when the server starts. This is the recommended way to initialize JustAnalytics.
Zero-config (reads from environment variables):
// instrumentation.ts (project root)
export { register } from '@justanalyticsapp/node/next';
With explicit configuration:
// instrumentation.ts
import { withJustAnalytics } from '@justanalyticsapp/node/next';
export function register() {
withJustAnalytics({
siteId: process.env.JA_SITE_ID!,
apiKey: process.env.JA_API_KEY!,
serviceName: 'my-next-app',
environment: process.env.NODE_ENV,
});
}
Add your credentials to .env.local:
JA_SITE_ID=site_abc123
JA_API_KEY=ja_sk_your_api_key_here
Add API route tracing
Wrap your API route handlers with withTracing to automatically create spans for each request:
// app/api/users/[id]/route.ts
import { withTracing } from '@justanalyticsapp/node/next';
export const GET = withTracing(async (req, { params }) => {
const { id } = await params;
const user = await db.users.findUnique({ where: { id } });
return Response.json(user);
});
export const POST = withTracing(async (req) => {
const body = await req.json();
const user = await db.users.create({ data: body });
return Response.json(user, { status: 201 });
});
Add middleware tracing (optional)
Trace your Next.js middleware to measure authentication, redirects, and other middleware logic:
// middleware.ts
import { withMiddlewareTracing } from '@justanalyticsapp/node/next';
import { NextResponse } from 'next/server';
export default withMiddlewareTracing(async (req) => {
// Your middleware logic
const token = req.cookies.get('session');
if (!token && req.nextUrl.pathname.startsWith('/dashboard')) {
return NextResponse.redirect(new URL('/login', req.url));
}
return NextResponse.next();
});
export const config = {
matcher: ['/dashboard/:path*'],
};
Add Server Component tracing (optional)
Trace data fetching in Server Components:
// app/users/[id]/page.tsx
import { traceServerComponent } from '@justanalyticsapp/node/next';
export default async function UserPage({ params }: { params: { id: string } }) {
return traceServerComponent('UserPage', async () => {
const user = await getUser(params.id);
return (
<div>
<h1>{user.name}</h1>
<p>{user.email}</p>
</div>
);
});
}
Add client-side tracking (optional)
For frontend analytics (page views, clicks, Web Vitals), add the tracking script to your root layout:
// app/layout.tsx
import Script from 'next/script';
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
{children}
<Script
src="https://justanalytics.app/tracker.js"
data-site-id="site_abc123"
crossOrigin="anonymous"
strategy="afterInteractive"
/>
<Script
src="https://justanalytics.app/monitor.js"
data-site-id="site_abc123"
crossOrigin="anonymous"
strategy="afterInteractive"
/>
</body>
</html>
);
}
next/script is what Next.js recommends for third-party scripts. A plain tag
works too, as long as it carries defer or async — without one of those,
@next/next/no-sync-scripts will fail your lint:
<script
defer
data-site-id="site_abc123"
crossorigin="anonymous"
src="https://justanalytics.app/monitor.js"
/>
crossOrigin="anonymous" is worth keeping. Without it the browser hides the
details of any error thrown inside the script, and it reaches your dashboard as
an unactionable Script error. with no stack.
Verify Your Setup
Send a request to any API route, then check the Traces tab in your dashboard. You should see server spans with route names like 'GET /api/users/[id]'.
Open DashboardAdvanced Features#
Error tracking#
Errors thrown inside withTracing handlers are automatically captured. You can also capture errors manually:
import JA from '@justanalyticsapp/node';
try {
await riskyOperation();
} catch (error) {
JA.captureException(error, {
tags: { module: 'payments' },
extra: { orderId: '12345' },
});
}
Structured logging#
import JA from '@justanalyticsapp/node';
JA.logger.info('User signed up', { userId: 'u123', plan: 'pro' });
JA.logger.error('Payment failed', { orderId: 'o456', reason: 'insufficient_funds' });
Custom spans#
import JA from '@justanalyticsapp/node';
const result = await JA.startSpan('process-order', async (span) => {
span.setAttribute('order.id', orderId);
span.setAttribute('order.total', total);
return await processOrder(orderId);
});
User identification#
import JA from '@justanalyticsapp/node';
// In your auth middleware or after login
JA.setUser({ id: user.id, email: user.email });
Custom metrics#
import JA from '@justanalyticsapp/node';
JA.recordMetric('custom.queue_size', 42, { queue: 'emails' });
Environment configuration#
| Variable | Description | Required |
|----------|-------------|----------|
| JA_SITE_ID | Your site ID from the dashboard | Yes |
| JA_API_KEY | API key (starts with ja_sk_) | Yes |
| JA_SERVICE_NAME | Service name for traces | No (defaults to package name) |
| JA_ENVIRONMENT | Deployment environment | No (defaults to NODE_ENV) |
Pages Router support#
If you are using the Pages Router, initialize the SDK in a custom server or at the top of _app.tsx:
// pages/_app.tsx
import JA from '@justanalyticsapp/node';
if (typeof window === 'undefined') {
JA.init({
siteId: process.env.JA_SITE_ID!,
apiKey: process.env.JA_API_KEY!,
serviceName: 'my-next-app',
});
}
export default function App({ Component, pageProps }) {
return <Component {...pageProps} />;
}
For API routes in Pages Router, wrap handlers manually:
// pages/api/users.ts
import JA from '@justanalyticsapp/node';
import type { NextApiRequest, NextApiResponse } from 'next';
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
return JA.startSpan(`${req.method} /api/users`, async (span) => {
const users = await db.users.findMany();
res.json(users);
});
}