Skip to main
← Index

Install Walityk on Next.js

Integrate the Walityk tag in a Next.js app (App Router or Pages Router) with next/script, SPA navigation tracking included.

This guide covers installing Walityk in a Next.js application, App Router (13+) or Pages Router. No package to install: Walityk integrates with a snippet and the next/script component. Plan for 10 minutes.

1. Create your site in the dashboard

  1. Sign in to the Walityk dashboard.
  2. Sites → New site: enter your domain (e.g. app.example.com) and pick the Custom (HTML/JS) platform.
  3. Click Create site. The site page’s Installation tab shows your snippet with your site ID already filled in.

The declared domain is what authorizes your requests (CORS): the exact domain and its subdomains are accepted automatically. If you track several distinct domains, create one site per domain.

2. Add the tag — App Router

In app/layout.tsx, add the snippet via next/script:

import Script from 'next/script';

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body>
        {children}
        <Script id="walityk" strategy="afterInteractive">
          {`(function(s,i,e){
  s.__tftq=s.__tftq||[];
  s.__tftq.push(['init',{site_id:i,endpoint:e+'/collect'}]);
  var t=document.createElement('script');
  t.async=true;
  t.src=e+'/tag.js';
  document.head.appendChild(t);
})(window,'YOUR_SITE_ID','https://tag.walityk.com');`}
        </Script>
      </body>
    </html>
  );
}

Replace YOUR_SITE_ID with the site ID shown in the dashboard (or copy the generated snippet directly — it is identical).

The tag emits automatically on load: page_view (with full context: device, viewport, network, performance), scroll milestones (25/50/75/90%), outbound clicks and form_submit. If you already have a GTM dataLayer, its events are picked up automatically.

Pages Router (alternative)

Same snippet, in pages/_app.tsx:

import type { AppProps } from 'next/app';
import Script from 'next/script';

export default function App({ Component, pageProps }: AppProps) {
  return (
    <>
      <Script id="walityk" strategy="afterInteractive">
        {`(function(s,i,e){
  s.__tftq=s.__tftq||[];
  s.__tftq.push(['init',{site_id:i,endpoint:e+'/collect'}]);
  var t=document.createElement('script');
  t.async=true;
  t.src=e+'/tag.js';
  document.head.appendChild(t);
})(window,'YOUR_SITE_ID','https://tag.walityk.com');`}
      </Script>
      <Component {...pageProps} />
    </>
  );
}

3. Track SPA navigations

Next.js navigates client-side without reloading the page: the tag emits the initial page_view, but route changes must be reported to it. Create a small client component:

// app/walityk-page-views.tsx
'use client';

import { usePathname, useSearchParams } from 'next/navigation';
import { useEffect, useRef } from 'react';

export function WalitykPageViews() {
  const pathname = usePathname();
  const searchParams = useSearchParams();
  const isFirst = useRef(true);

  useEffect(() => {
    // The tag emits the initial page_view itself on load: don't double it.
    if (isFirst.current) {
      isFirst.current = false;
      return;
    }
    window.__tftq = window.__tftq || [];
    window.__tftq.push(['track', 'page_view']);
  }, [pathname, searchParams]);

  return null;
}

Mount it in app/layout.tsx (the <Suspense> is required by useSearchParams):

import { Suspense } from 'react';
import { WalitykPageViews } from './walityk-page-views';

// … inside <body>:
<Suspense fallback={null}>
  <WalitykPageViews />
</Suspense>

With the Pages Router, use the router’s routeChangeComplete event in _app.tsx:

const router = useRouter();
useEffect(() => {
  const onRoute = () => {
    window.__tftq = window.__tftq || [];
    window.__tftq.push(['track', 'page_view']);
  };
  router.events.on('routeChangeComplete', onRoute);
  return () => router.events.off('routeChangeComplete', onRoute);
}, [router.events]);

TypeScript: declare the queue once in a global.d.ts file: declare global { interface Window { __tftq?: unknown[][] } } export {};

4. Track conversions

Push an event via window.__tftq.push(['track', type, properties]) — the call works whether the tag has loaded yet or not:

'use client';

export function SignupButton() {
  return (
    <button
      onClick={() => {
        window.__tftq = window.__tftq || [];
        window.__tftq.push(['track', 'sign_up', { plan: 'starter' }]);
      }}
    >
      Sign up
    </button>
  );
}

For a purchase, on the confirmation page:

window.__tftq.push([
  'track',
  'purchase',
  { value: 49.0, currency: 'EUR', transaction_id: 'ORD-1234' },
]);

The value, currency and transaction_id properties are forwarded as-is to your destinations (GA4, Meta CAPI, Google Ads…).

Standard event types: page_view, view_item, add_to_cart, begin_checkout, purchase, sign_up, login, lead. Any other name goes through as a custom event.

By default the tag runs in cookieless mode: no cookies, no persistent identifier — no banner required. If you have a CMP (Cookiebot, Axeptio, Didomi…), switch to banner mode by adding ,mode:'banner' to the snippet’s init object: the tag starts in Consent Mode v2 deny-default, auto-detects the CMP and only releases events after the visitor’s choice.

6. Verify

  1. In the dashboard, open your site’s page → View live events.
  2. Browse your app (locally or in production): page_view events appear in real time.
  3. Trigger a conversion and check it comes through, then check GA4 Realtime if the destination is connected.

Not working?

  • No /collect request in DevTools → Network: the snippet isn’t mounted (check the <Script> is in the root layout) or the site_id is empty.
  • /collect request blocked by CORS: the domain the app runs on doesn’t match the domain declared in the dashboard. Locally, test on the declared production or preview domain instead.
  • Strict CSP: add https://tag.walityk.com to script-src and connect-src.
  • Only one page_view per session: the step 3 component isn’t mounted — SPA navigations aren’t being reported.

What’s next?

  • Connect your destinations (GA4, Meta CAPI, Google Ads…) from Destinations — no code changes.
  • Add your custom events to the site’s tracking plan.

Need help? hi@walityk.com.