Next.js Analytics and Monitoring
Integrate analytics, error tracking, and performance monitoring into your Next.js app.
TL;DR
- 01Use web-vitals library to measure Core Web Vitals automatically.
- 02Integrate Sentry to capture and track errors in production.
- 03Add Google Analytics to monitor user behavior and page views.
Tips
- 01Use web-vitals and Sentry together for complete visibility into user experience and error rates.
Warnings
- 01Be mindful of privacy when tracking user data — follow GDPR and CCPA regulations when storing user information.
Web Vitals
- Use
useReportWebVitalsfromnext/vitalsto capture Core Web Vitals in App Router."use client"; import { useReportWebVitals } from "next/vitals"; export function WebVitals() { useReportWebVitals((metric) => { console.log(metric); // { name, value, id, ... } }); return null; } // Place <WebVitals /> in app/layout.tsx - Core Web Vitals: CLS, INP (replaced FID), LCP, FCP, TTFB.
- Essential metrics for user experience and Google SEO ranking.
- Send vitals to a custom analytics endpoint.
import { getCLS, getINP, getLCP } from "web-vitals"; function sendToAnalytics({ name, value, id }) { fetch("/api/vitals", { method: "POST", body: JSON.stringify({ name, value, id }) }); } getCLS(sendToAnalytics); getINP(sendToAnalytics); getLCP(sendToAnalytics); - For Pages Router, export
reportWebVitalsfrompages/_app.js.// pages/_app.js — Pages Router only export function reportWebVitals(metric) { console.log(metric); // { name, value, id, startTime, ... } }
Error Tracking with Sentry
- Integrate Sentry for error monitoring.
npm install @sentry/nextjs - Set up Sentry in your Next.js config.
// next.config.js const { withSentryConfig } = require("@sentry/nextjs"); module.exports = withSentryConfig({ // your Next.js config }, { org: "your-org", project: "your-project", authToken: process.env.SENTRY_AUTH_TOKEN }); - Capture errors automatically on client and server.
import * as Sentry from "@sentry/nextjs"; Sentry.captureException(error); - Capture exceptions inside error boundaries manually.
// app/error.tsx "use client"; import * as Sentry from "@sentry/nextjs"; import { useEffect } from "react"; export default function Error({ error, reset }) { useEffect(() => { Sentry.captureException(error); }, [error]); return <button onClick={reset}>Try again</button>; } - Add user context to Sentry for better debugging.
Sentry.setUser({ id: user.id, email: user.email });
Google Analytics
- Add Google Analytics for user tracking.
// app/layout.tsx import Script from "next/script"; export default function RootLayout({ children }) { return ( <html> <head> <Script src="https://www.googletagmanager.com/gtag/js?id=GA_ID" strategy="afterInteractive" /></Script> </head> <body>{children}</body> </html> ); } - Track page views and custom events.
function trackEvent(name: string) { window.gtag('event', name); } - Track page views on route changes in App Router.
"use client"; import { usePathname } from "next/navigation"; import { useEffect } from "react"; export function Analytics() { const pathname = usePathname(); useEffect(() => { window.gtag?.("event", "page_view", { page_path: pathname }); }, [pathname]); return null; } - Track ecommerce events for purchases and conversions.
function trackPurchase(order) { window.gtag("event", "purchase", { transaction_id: order.id, value: order.total, currency: "USD" }); } - Use Google Tag Manager for managing multiple scripts.
<Script src="https://www.googletagmanager.com/gtm.js?id=GTM-XXXX" strategy="afterInteractive" />
Custom Metrics
- Measure custom metrics with events.
// Track API response times export async function POST(request: Request) { const start = performance.now(); try { const result = await processRequest(request); const duration = performance.now() - start; // Log metric console.log(`Request took ${duration}ms`); return Response.json(result); } catch (error) { Sentry.captureException(error); return Response.json({ error: "Failed" }, { status: 500 }); } } - Send metrics to monitoring service.
async function recordMetric(name: string, value: number) { await fetch("/api/metrics", { method: "POST", body: JSON.stringify({ name, value }) }); } - Store custom metrics in a database via API route.
// app/api/metrics/route.ts export async function POST(request: Request) { const { name, value } = await request.json(); await db.metric.create({ data: { name, value, at: new Date() } }); return Response.json({ ok: true }); } - Track user interactions like button clicks or searches.
function trackSearch(query: string) { fetch("/api/metrics", { method: "POST", body: JSON.stringify({ name: "search", value: query.length }) }); } - Use the Performance API to measure render times.
performance.mark("component-start"); // ... render performance.mark("component-end"); performance.measure("component", "component-start", "component-end");
Performance Monitoring
- Use Lighthouse CI to monitor performance.
npm install -g @lhci/cli@* lhci autorun - Monitor bundle size with package.json scripts.
{ "scripts": { "analyze": "ANALYZE=true npm run build" } } - Set up performance budgets.
// next.config.js const withBundleAnalyzer = require("@next/bundle-analyzer")({ enabled: process.env.ANALYZE === "true" }); - Run Lighthouse CI in a GitHub Actions workflow.
- name: Run Lighthouse CI run: lhci autorun env: LHCI_GITHUB_APP_TOKEN: ${{ secrets.LHCI_TOKEN }} - Set performance score thresholds to fail CI on regressions.
{ "assert": { "assertions": { "categories:performance": ["error", { "minScore": 0.9 }] } } }
FAQ
In the App Router, import useReportWebVitals from next/vitals and call it inside a Client Component placed in your root layout — Next.js passes each metric (CLS, INP, LCP, FCP, TTFB) to your callback. For the Pages Router, export a reportWebVitals function from pages/_app.js instead.
Install @sentry/nextjs and run npx @sentry/wizard@latest -i nextjs to auto-configure sentry.client.config.js, sentry.server.config.js, and sentry.edge.config.js. The wizard also wraps your next.config.js with withSentryConfig to enable source map uploads for readable stack traces.
Yes — create a client component that loads the gtag script via next/script with strategy='afterInteractive' and place it in your root layout. Call gtag('event', ...) for custom events, and use usePathname with a useEffect to fire page view events on route changes.
reportWebVitals captures browser-measured paint and layout metrics (CLS, LCP, etc.) that reflect perceived page speed, while Sentry performance monitoring traces request durations, API calls, and server-side spans across the full stack. Use both together to correlate slow backend operations with poor front-end user experience scores.
Use the performance.mark() and performance.measure() Web APIs in client components to instrument specific interactions, then read results with performance.getEntriesByType('measure') and send them to your own /api/metrics endpoint. For server-side timing, use Date.now() around data-fetching logic inside Server Components or Route Handlers and log to your observability backend.