Vercel Analytics
"Vercel Analytics: Web Vitals, page views, custom events, audience insights, Next.js integration, Speed Insights"
Vercel Analytics provides first-party, privacy-friendly analytics deeply integrated with the Next.js deployment platform. It consists of two complementary products: Web Analytics for page views and visitor metrics, and Speed Insights for Core Web Vitals performance monitoring. Since data is collected as a first-party integration, there are no cookie banners or ad-blocker ## Key Points - **Use the `beforeSend` callback** to strip sensitive query parameters and normalize dynamic - **Keep custom event property values as strings.** The Vercel Analytics API accepts string - **Add both `Analytics` and `SpeedInsights` components.** They serve different purposes: - **Return `null` from `beforeSend`** to exclude internal pages, bot traffic, or admin sessions - **Use `mode="development"`** during local development to see events in the console without - **Leverage the Vercel dashboard filters** for deployment-specific analysis. You can compare - **Track server-side events** for critical conversions like checkout completions to ensure they - **Sending too many unique custom events.** Vercel Analytics has limits on event volume for - **Including PII in event properties.** Never pass email addresses, user IDs, or other - **Wrapping the Analytics component in conditional logic.** The component handles environment - **Ignoring the `beforeSend` callback.** Without it, URLs with tokens, session IDs, or other - **Using Vercel Analytics outside of Vercel.** The analytics service requires Vercel hosting
skilldb get analytics-services-skills/Vercel AnalyticsFull skill: 335 linesVercel Analytics
Core Philosophy
Vercel Analytics provides first-party, privacy-friendly analytics deeply integrated with the Next.js deployment platform. It consists of two complementary products: Web Analytics for page views and visitor metrics, and Speed Insights for Core Web Vitals performance monitoring. Since data is collected as a first-party integration, there are no cookie banners or ad-blocker issues. The philosophy is zero-config analytics that "just works" when deployed to Vercel, with optional custom event tracking for deeper product understanding. Speed Insights measures real user performance metrics (LCP, FID, CLS, TTFB, INP) so teams can monitor and improve the experience for actual visitors rather than relying on synthetic lab tests.
Setup
Installation
// Install both packages
// npm install @vercel/analytics @vercel/speed-insights
// app/layout.tsx — Add both components
import { Analytics } from "@vercel/analytics/react";
import { SpeedInsights } from "@vercel/speed-insights/next";
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
{children}
<Analytics />
<SpeedInsights />
</body>
</html>
);
}
Configuration Options
// app/layout.tsx — With configuration
import { Analytics } from "@vercel/analytics/react";
import { SpeedInsights } from "@vercel/speed-insights/next";
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
{children}
<Analytics
mode={process.env.NODE_ENV === "development" ? "development" : "production"}
debug={false}
beforeSend={(event) => {
// Strip query parameters containing sensitive data
const url = new URL(event.url);
url.searchParams.delete("token");
url.searchParams.delete("email");
return { ...event, url: url.toString() };
}}
/>
<SpeedInsights
route={undefined} // Auto-detected in Next.js
debug={false}
/>
</body>
</html>
);
}
Pages Router Setup
// pages/_app.tsx — For Pages Router projects
import type { AppProps } from "next/app";
import { Analytics } from "@vercel/analytics/react";
import { SpeedInsights } from "@vercel/speed-insights/next";
export default function App({ Component, pageProps }: AppProps) {
return (
<>
<Component {...pageProps} />
<Analytics />
<SpeedInsights />
</>
);
}
Key Techniques
Custom Event Tracking
// lib/analytics.ts
import { track } from "@vercel/analytics";
// Track a signup event
export function trackSignup(method: "email" | "google" | "github"): void {
track("signup", { method });
}
// Track a feature interaction
export function trackFeatureUsage(feature: string, variant?: string): void {
track("feature_used", {
feature,
variant: variant ?? "default",
path: window.location.pathname,
});
}
// Track a purchase
export function trackPurchase(plan: string, amount: number, interval: string): void {
track("purchase", {
plan,
amount: String(amount),
interval,
});
}
// Track search usage
export function trackSearch(query: string, resultCount: number): void {
track("search", {
query_length: String(query.length),
result_count: String(resultCount),
has_results: String(resultCount > 0),
});
}
React Hook for Tracking
// hooks/useAnalytics.ts
"use client";
import { useCallback } from "react";
import { track } from "@vercel/analytics";
import { usePathname } from "next/navigation";
interface AnalyticsHook {
trackEvent: (name: string, properties?: Record<string, string>) => void;
trackClick: (element: string, properties?: Record<string, string>) => void;
trackFormSubmit: (formName: string, success: boolean) => void;
}
export function useAnalytics(): AnalyticsHook {
const pathname = usePathname();
const trackEvent = useCallback(
(name: string, properties?: Record<string, string>) => {
track(name, { ...properties, path: pathname });
},
[pathname]
);
const trackClick = useCallback(
(element: string, properties?: Record<string, string>) => {
track("click", { element, ...properties, path: pathname });
},
[pathname]
);
const trackFormSubmit = useCallback(
(formName: string, success: boolean) => {
track("form_submit", {
form: formName,
success: String(success),
path: pathname,
});
},
[pathname]
);
return { trackEvent, trackClick, trackFormSubmit };
}
URL Sanitization with beforeSend
// lib/analytics-config.ts
import type { BeforeSendEvent } from "@vercel/analytics";
export function sanitizeAnalyticsEvent(event: BeforeSendEvent): BeforeSendEvent | null {
const url = new URL(event.url);
// Remove sensitive query parameters
const sensitiveParams = ["token", "session", "email", "key", "secret"];
sensitiveParams.forEach((param) => url.searchParams.delete(param));
// Block tracking for internal/admin paths
if (url.pathname.startsWith("/admin") || url.pathname.startsWith("/internal")) {
return null; // Returning null drops the event entirely
}
// Normalize dynamic route segments for cleaner grouping
const normalizedPath = url.pathname
.replace(/\/users\/[^/]+/, "/users/[id]")
.replace(/\/posts\/[^/]+/, "/posts/[slug]");
url.pathname = normalizedPath;
return { ...event, url: url.toString() };
}
Server-Side Event Tracking
// app/api/checkout/route.ts
import { track } from "@vercel/analytics/server";
import { NextRequest, NextResponse } from "next/server";
export async function POST(request: NextRequest): Promise<NextResponse> {
const body = await request.json();
// Process the checkout logic
const order = await processCheckout(body);
// Track the conversion server-side
await track("checkout_completed", {
order_id: order.id,
plan: order.plan,
amount: String(order.amount),
});
return NextResponse.json({ orderId: order.id });
}
async function processCheckout(body: unknown): Promise<{ id: string; plan: string; amount: number }> {
// Checkout processing logic
return { id: "ord_123", plan: "pro", amount: 29 };
}
Speed Insights with Custom Reporting
// lib/vitals.ts
import type { Metric } from "web-vitals";
// Custom Web Vitals reporter for additional monitoring
export function reportVitals(metric: Metric): void {
const body = {
name: metric.name,
value: metric.value,
rating: metric.rating, // "good" | "needs-improvement" | "poor"
delta: metric.delta,
id: metric.id,
navigationType: metric.navigationType,
url: window.location.href,
};
// Send to your own monitoring endpoint alongside Vercel
if (metric.rating === "poor") {
fetch("/api/vitals-alert", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(body),
});
}
}
// pages/_app.tsx (Pages Router) — Export the reporter
export function reportWebVitals(metric: Metric): void {
reportVitals(metric);
}
Environment-Aware Analytics Wrapper
// lib/track.ts
import { track as vercelTrack } from "@vercel/analytics";
type Properties = Record<string, string | number | boolean>;
export function analyticsTrack(event: string, properties?: Properties): void {
const stringProps: Record<string, string> = {};
if (properties) {
for (const [key, value] of Object.entries(properties)) {
stringProps[key] = String(value);
}
}
if (process.env.NODE_ENV === "development") {
console.log(`[Analytics] ${event}`, stringProps);
return;
}
vercelTrack(event, stringProps);
}
Best Practices
- Use the
beforeSendcallback to strip sensitive query parameters and normalize dynamic route segments before data reaches the Vercel dashboard. - Keep custom event property values as strings. The Vercel Analytics API accepts string values only. Convert numbers and booleans to strings explicitly.
- Add both
AnalyticsandSpeedInsightscomponents. They serve different purposes: Analytics tracks visitor behavior, Speed Insights monitors performance. Use both. - Return
nullfrombeforeSendto exclude internal pages, bot traffic, or admin sessions from your analytics data entirely. - Use
mode="development"during local development to see events in the console without sending them to the Vercel API. - Leverage the Vercel dashboard filters for deployment-specific analysis. You can compare performance between preview and production deployments automatically.
- Track server-side events for critical conversions like checkout completions to ensure they are recorded even if the client navigates away before the event fires.
Anti-Patterns
- Sending too many unique custom events. Vercel Analytics has limits on event volume for free and Pro plans. Focus on 10-20 meaningful events rather than tracking every click.
- Including PII in event properties. Never pass email addresses, user IDs, or other personally identifiable information as event properties.
- Wrapping the Analytics component in conditional logic. The component handles environment detection internally. Conditionally rendering it can cause missed page views.
- Ignoring the
beforeSendcallback. Without it, URLs with tokens, session IDs, or other sensitive data in query strings are sent to the analytics dashboard in plain text. - Using Vercel Analytics outside of Vercel. The analytics service requires Vercel hosting for data collection. Self-hosted Next.js deployments cannot use it.
- Not monitoring Speed Insights regressions. Performance metrics are collected continuously. Set up alerts or review the dashboard regularly to catch regressions introduced by new deployments before they affect too many users.
- Passing numeric values directly. The
track()function requires string property values. Passing numbers causes silent failures where events are sent without their properties.
Install this skill directly: skilldb add analytics-services-skills
Related Skills
Amplitude Analytics
"Amplitude: product analytics, behavioral cohorts, funnels, retention, experiment platform, user journeys, JavaScript SDK"
Heap
Heap: autocapture product analytics, session replay, retroactive event definition, funnel and retention analysis, JavaScript SDK
June.so
June.so: B2B product analytics, company-level insights, feature adoption reports, activation tracking, Node and JavaScript SDK
Mixpanel Analytics
"Mixpanel: event-based analytics, funnels, retention, user profiles, cohorts, group analytics, JavaScript/Node SDK"
Plausible Analytics
"Plausible: privacy-friendly analytics, no cookies, lightweight script, custom events, goals, API, self-hosted option"
PostHog Analytics
"PostHog: product analytics, event tracking, feature flags, session recordings, A/B testing, self-hosted/cloud, Next.js SDK"