Skip to Content
How-to guideSourcesAvailable

Next.js

Integrate Vendo web tracking with Next.js — App Router and Pages Router setup with client-side navigation tracking.

Last reviewed July 25, 2026

Add Vendo tracking to your Next.js app. Both App Router and Pages Router are supported.

App Router

Add the CDN snippet to your root layout using Next.js’s <Script> component:

// app/layout.tsx import Script from 'next/script'; export default function RootLayout({ children, }: { children: React.ReactNode; }) { return ( <html lang="en"> <head> <Script id="vendo-tracking" strategy="afterInteractive"> {` (function(w,d,s,e,l,k){w['VendoObject']=e;w[e]=w[e]||function(){ (w[e].q=w[e].q||[]).push(arguments)};w[e].l=1*new Date(); l=d.createElement(s);k=d.getElementsByTagName(s)[0]; l.async=1;l.src='https://cdn.vendodata.com/sdk/v1/vendo.js';k.parentNode.insertBefore(l,k); })(window,document,'script','vendo'); vendo('init', 'YOUR_WRITE_KEY', { host: 'https://YOUR_TRACKING_ENDPOINT' }); `} </Script> </head> <body>{children}</body> </html> ); }

Track Client-Side Navigations

Page views are explicit — since SDK 0.2.0 init never sends one automatically. Add a route-change listener component; it sends one canonical Page Viewed event for the initial route and each client-side pathname or query-string change:

// components/track-page-views.tsx 'use client'; import { usePathname, useSearchParams } from 'next/navigation'; import { useEffect } from 'react'; declare global { interface Window { vendo?: | ((...args: unknown[]) => void) | { page: (name?: string, properties?: Record<string, unknown>) => void; identify: (userId: string, traits?: Record<string, unknown>) => void; reset: () => void; }; } } function trackPageView(pathname: string, search: string) { const page = search ? `${pathname}?${search}` : pathname; const properties = { path: pathname, search, page }; if (typeof window.vendo === 'function') { window.vendo('page', 'Page Viewed', properties); } else { window.vendo?.page('Page Viewed', properties); } } export function TrackPageViews() { const pathname = usePathname(); const searchParams = useSearchParams(); const search = searchParams.toString(); useEffect(() => { if (!window.vendo) return; trackPageView(pathname, search); }, [pathname, search]); return null; }

Add <TrackPageViews /> to your root layout inside a <Suspense> boundary (required by useSearchParams):

// app/layout.tsx (body section) import { Suspense } from 'react'; import { TrackPageViews } from '@/components/track-page-views'; <body> <Suspense fallback={null}> <TrackPageViews /> </Suspense> {children} </body>;

Pages Router

Add the snippet to _app.tsx and track route changes with the Next.js router:

// pages/_app.tsx import type { AppProps } from 'next/app'; import Script from 'next/script'; import { useRouter } from 'next/router'; import { useEffect } from 'react'; export default function App({ Component, pageProps }: AppProps) { const router = useRouter(); useEffect(() => { const trackCurrentPage = (url: string) => { const nextUrl = new URL(url, window.location.origin); trackPageView(nextUrl.pathname, nextUrl.search.slice(1)); }; trackCurrentPage(window.location.href); router.events.on('routeChangeComplete', trackCurrentPage); return () => router.events.off('routeChangeComplete', trackCurrentPage); }, [router.events]); return ( <> <Script id="vendo-tracking" strategy="afterInteractive"> {` (function(w,d,s,e,l,k){w['VendoObject']=e;w[e]=w[e]||function(){ (w[e].q=w[e].q||[]).push(arguments)};w[e].l=1*new Date(); l=d.createElement(s);k=d.getElementsByTagName(s)[0]; l.async=1;l.src='https://cdn.vendodata.com/sdk/v1/vendo.js';k.parentNode.insertBefore(l,k); })(window,document,'script','vendo'); vendo('init', 'YOUR_WRITE_KEY', { host: 'https://YOUR_TRACKING_ENDPOINT' }); `} </Script> <Component {...pageProps} /> </> ); }

Identify Users

Call identify once the user is authenticated:

const authUserId = user.id; // Use the same stable ID sent to Mixpanel. if (typeof window.vendo === 'function') { window.vendo('identify', authUserId, { plan: user.plan }); } else { window.vendo?.identify(authUserId, { plan: user.plan }); }

Call reset on logout to clear the identity:

if (typeof window.vendo === 'function') { window.vendo('reset'); } else { window.vendo?.reset(); }

If Mixpanel is also installed, call mixpanel.identify(authUserId) and mixpanel.reset() at the same lifecycle points. This lets Vendo events and Mixpanel browser activity resolve to the same user profile. Use an opaque stable application ID rather than an email address as the primary identity key.


Need help?

Include your workspace, integration or job ID, and the first error message when you contact support.

support@vendodata.com
Last updated on