Zum Hauptinhalt springen
Kostenlos15 minutesFortgeschrittene

Next.js-Integration

Integrieren Sie Zenovay mit Next.js – App Router, Pages Router und serverseitiges Tracking. Erfahren Sie mehr über nextjs in diesem API-Integrationsleitfaden.

nextjsreactintegrationssrapp-router
Zuletzt aktualisiert:

Integrieren Sie Zenovay Analytics in Ihre Next.js-Anwendung mit Unterstützung für sowohl App Router als auch Pages Router mithilfe des Tracking-Script-Tags.

Installation

Es wird kein npm-Paket benötigt. Fügen Sie das Zenovay-Tracking-Script Ihrer App mithilfe der Next.js Script-Komponente oder eines Standard-<script>-Tags hinzu.

Sie finden Ihren Tracking-Code in der Zenovay-App: Öffnen Sie Domains, klicken Sie auf Ihre Website und öffnen Sie dann deren Seite Allgemeine Einstellungen. Die Karte Tracking-Script zeigt das Snippet und eine Schaltfläche Installation verifizieren.

App Router-Einrichtung (Next.js 13+)

Zum Root-Layout hinzufügen

// 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://api.zenovay.com/z.js"
          data-tracking-code="YOUR_TRACKING_CODE"
          strategy="afterInteractive"
        />
      </body>
    </html>
  );
}

Mit Umgebungsvariable

// app/layout.tsx
import Script from 'next/script';

export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <body>
        {children}
        <Script
          src="https://api.zenovay.com/z.js"
          data-tracking-code={process.env.NEXT_PUBLIC_ZENOVAY_TRACKING_CODE}
          strategy="afterInteractive"
        />
      </body>
    </html>
  );
}

Client-Komponente für Events

Erstellen Sie eine Client-Komponente zum Tracking von Seitenaufrufen bei Client-seitiger (SPA) Navigation:

// components/Analytics.tsx
'use client';

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

export function Analytics() {
  const pathname = usePathname();
  const searchParams = useSearchParams();

  useEffect(() => {
    // Track page view on route change (SPA navigation)
    if (window.zenovay) {
      window.zenovay('page');
    }
  }, [pathname, searchParams]);

  return null;
}

Zum Layout hinzufügen:

// app/layout.tsx
import Script from 'next/script';
import { Analytics } from '@/components/Analytics';

export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <body>
        {children}
        <Analytics />
        <Script
          src="https://api.zenovay.com/z.js"
          data-tracking-code="YOUR_TRACKING_CODE"
          strategy="afterInteractive"
        />
      </body>
    </html>
  );
}

Pages Router-Einrichtung

Zu _app.tsx hinzufügen

// pages/_app.tsx
import Script from 'next/script';
import type { AppProps } from 'next/app';

export default function App({ Component, pageProps }: AppProps) {
  return (
    <>
      <Component {...pageProps} />
      <Script
        src="https://api.zenovay.com/z.js"
        data-tracking-code="YOUR_TRACKING_CODE"
        strategy="afterInteractive"
      />
    </>
  );
}

Oder Script in _document.tsx verwenden

// pages/_document.tsx
import { Html, Head, Main, NextScript } from 'next/document';
import Script from 'next/script';

export default function Document() {
  return (
    <Html>
      <Head />
      <body>
        <Main />
        <NextScript />
        <Script
          src="https://api.zenovay.com/z.js"
          data-tracking-code="YOUR_TRACKING_CODE"
          strategy="afterInteractive"
        />
      </body>
    </Html>
  );
}

Event-Tracking

Client-Komponente

'use client';

export function SignupButton() {
  const handleClick = () => {
    if (window.zenovay) {
      window.zenovay('track', 'signup_click', {
        plan: 'pro',
        source: 'pricing'
      });
    }
  };

  return (
    <button onClick={handleClick}>
      Start Free Trial
    </button>
  );
}

Benutzeridentifikation

Nach der Authentifizierung

'use client';

import { useEffect } from 'react';
import { useSession } from 'next-auth/react';

export function UserIdentifier() {
  const { data: session } = useSession();

  useEffect(() => {
    if (session?.user && window.zenovay) {
      window.zenovay('identify', session.user.id, {
        email: session.user.email,
        name: session.user.name
      });
    }
  }, [session]);

  return null;
}

Umsatz-Tracking

E-Commerce-Checkout

'use client';

import { useEffect } from 'react';

export function OrderConfirmation({ order }) {
  useEffect(() => {
    if (window.zenovay) {
      window.zenovay('revenue', order.total, 'USD', {
        order_id: order.id,
        items: order.items
      });
    }
  }, [order.id]);

  return (
    <div>
      <h1>Order Confirmed!</h1>
    </div>
  );
}

Information

Bei Käufen, die von Ihrem Backend erfasst werden (z. B. ein Stripe-Webhook), melden Sie den Umsatz stattdessen vom Server aus – siehe Server-Side Tracking weiter unten. Dies gewährleistet eine genaue Umsatzzuordnung auch dann, wenn der Browser nie eine Bestätigungsseite erreicht.

Serverseitiges Tracking

Für Events, bei denen Ihr Backend die Quelle der Wahrheit ist – Käufe, Anmeldungen, Aktivitäten außerhalb der Website – senden Sie diese vom Server mit dem Endpunkt POST /api/v1/events aus. Dies ist eine separate, authentifizierte Erfassungs-API.

Warnung

Server-seitige Erfassung erfordert einen API-Schlüssel und einen kostenpflichtigen Plan (Pro, Scale oder Enterprise). Kostenlose Konten erhalten 403 API_PAID_PLAN_REQUIRED. Erstellen Sie einen Schlüssel unter Settings → Security → API keys – siehe Getting an API Key. Das Browser-Tracking-Script oben bleibt kostenlos auf jedem Plan.

Authentifizieren Sie sich mit einem zv_* API-Schlüssel im Authorization: Bearer-Header. Jedes Event hat einen type (pageview, event, identify, goal oder purchase), einen Millisekunden-ts und ein typ-spezifisches props-Objekt. Übergeben Sie einen idempotencyKey, um Wiederholungen sicher zu machen.

Server Action

// app/actions.ts
'use server';

export async function submitForm(formData: FormData) {
  // Process form
  const email = formData.get('email');

  // Track a custom event server-side
  await fetch('https://api.zenovay.com/api/v1/events', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${process.env.ZENOVAY_API_KEY}`,
    },
    body: JSON.stringify({
      trackingCode: process.env.ZENOVAY_TRACKING_CODE,
      events: [{
        type: 'event',
        ts: Date.now(),
        props: {
          name: 'form_submitted',
          form: 'contact',
          has_email: !!email,
        },
        idempotencyKey: `form-${crypto.randomUUID()}`,
      }],
    }),
  });

  return { success: true };
}

API-Route-Handler

// app/api/track/route.ts
import { NextRequest, NextResponse } from 'next/server';

export async function POST(request: NextRequest) {
  const body = await request.json();

  await fetch('https://api.zenovay.com/api/v1/events', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${process.env.ZENOVAY_API_KEY}`,
    },
    body: JSON.stringify({
      trackingCode: process.env.ZENOVAY_TRACKING_CODE,
      events: [{
        type: 'event',
        ts: Date.now(),
        props: {
          name: body.name || 'custom_event',
          ...body.props,
        },
        idempotencyKey: crypto.randomUUID(),
      }],
      serverContext: {
        clientIp: request.headers.get('x-forwarded-for') ?? undefined,
        userAgent: request.headers.get('user-agent') ?? undefined,
      },
    })
  });

  return NextResponse.json({ success: true });
}

Information

pageview-Events benötigen eine echte RFC-4122 visitorId und sessionId (die Spalten sind UUID-typisiert), daher sind sie normalerweise am besten dem Browser-Tracker überlassen. Der Server-Endpunkt ist ideal für event, identify, goal und purchase-Typen. Siehe Server-Side Tracking für das vollständige Event-Schema und Ablehnungsgründe.

Kauf von einem Webhook

// app/api/webhooks/stripe/route.ts
import { NextResponse } from 'next/server';

export async function POST(request: Request) {
  const event = await request.json();

  // ... verify the Stripe signature first ...

  await fetch('https://api.zenovay.com/api/v1/events', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${process.env.ZENOVAY_API_KEY}`,
    },
    body: JSON.stringify({
      trackingCode: process.env.ZENOVAY_TRACKING_CODE,
      events: [{
        type: 'purchase',
        ts: Date.now(),
        // The visitorId you stored against this customer on your side
        visitorId: event.data.object.metadata?.visitor_id,
        props: {
          amount: event.data.object.amount_total / 100,
          currency: event.data.object.currency?.toUpperCase() || 'USD',
          payment_provider: 'stripe',
        },
        idempotencyKey: event.id,
      }],
    })
  });

  return NextResponse.json({ received: true });
}

Umgebungsvariablen

Einrichtung

# .env.local
NEXT_PUBLIC_ZENOVAY_TRACKING_CODE=your-tracking-code   # client-side script (public)
ZENOVAY_TRACKING_CODE=your-tracking-code               # server-side (same value)
ZENOVAY_API_KEY=zv_your-api-key                        # server-side only — never expose

Warnung

Nur der Tracking-Code (verwendet im data-tracking-code-Attribut) ist sicher, um mit dem Präfix NEXT_PUBLIC_ offenzulegen. Ihr zv_* API-Schlüssel muss auf der Serverseite bleiben – präfix ihn niemals mit NEXT_PUBLIC_.

Verwendung

// Client-side (in layout)
<Script
  src="https://api.zenovay.com/z.js"
  data-tracking-code={process.env.NEXT_PUBLIC_ZENOVAY_TRACKING_CODE}
  strategy="afterInteractive"
/>

// Server-side (in API routes or server actions)
const response = await fetch('https://api.zenovay.com/api/v1/events', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': `Bearer ${process.env.ZENOVAY_API_KEY}`,
  },
  body: JSON.stringify({
    trackingCode: process.env.ZENOVAY_TRACKING_CODE,
    events: [{ type: 'event', ts: Date.now(), props: { name: 'custom_event' } }],
  }),
});

Route Groups-Analytik

// app/(marketing)/layout.tsx
import Script from 'next/script';

export default function MarketingLayout({ children }) {
  return (
    <>
      {children}
      <Script
        src="https://api.zenovay.com/z.js"
        data-tracking-code="MARKETING_TRACKING_CODE"
        strategy="afterInteractive"
      />
    </>
  );
}

// app/(app)/layout.tsx
import Script from 'next/script';

export default function AppLayout({ children }) {
  return (
    <>
      {children}
      <Script
        src="https://api.zenovay.com/z.js"
        data-tracking-code="APP_TRACKING_CODE"
        strategy="afterInteractive"
      />
    </>
  );
}

TypeScript-Typen

// types/zenovay.d.ts
interface ZenovayFunction {
  (command: 'track', name: string, properties?: Record<string, unknown>): void;
  (command: 'identify', userId: string, traits?: Record<string, unknown>): void;
  (command: 'goal', name: string, properties?: Record<string, unknown>): void;
  (command: 'page'): void;
  (command: 'revenue', amount: number, currency: string, meta?: Record<string, unknown>): void;
}

declare global {
  interface Window {
    zenovay: ZenovayFunction;
  }
}

export {};

Häufige Muster

Mit Next-Auth

// app/providers.tsx
'use client';

import { SessionProvider } from 'next-auth/react';

export function Providers({ children }) {
  return (
    <SessionProvider>
      {children}
    </SessionProvider>
  );
}

ISR/SSG-Seiten

// app/blog/[slug]/page.tsx
export async function generateStaticParams() {
  const posts = await getPosts();
  return posts.map((post) => ({ slug: post.slug }));
}

export default async function BlogPost({ params }) {
  // Static generation - no server tracking here
  // Tracking happens on client via the Zenovay script tag
  const post = await getPost(params.slug);

  return <Article post={post} />;
}

Fehlerbehebung

Script wird nicht geladen

Überprüfen Sie:

  • Script befindet sich im Layout/Dokument
  • Keine Störung durch Werbeblocker
  • Tracking-Code ist korrekt

Keine Seitenaufrufe

Stellen Sie sicher:

  • Script wird nach dem Body geladen
  • Nicht im Entwicklungsmodus (es sei denn, dies ist beabsichtigt)
  • Routenänderungen werden erkannt

Hydration-Probleme

Verwenden Sie die afterInteractive-Strategie:

<Script
  src="https://api.zenovay.com/z.js"
  data-tracking-code="YOUR_TRACKING_CODE"
  strategy="afterInteractive"
/>

Nächste Schritte

War dieser Artikel hilfreich?