Rekomi Docs
Für MarkenTracking installieren
Brands

Auf Next.js App Router installieren

Nutze next/script in app/layout.tsx mit strategy="afterInteractive". Rohe Script-Tags in metadata werden entfernt. Übergib den Affiliate-Slug vom Client über window.Rekomi.getReferral().

Der App Router von Next.js 16 serialisiert das Dokument anders als der Pages Router, und die Metadata-API nimmt keine rohen <script>-Tags an (sie werden beim Serialisieren entfernt). Die vorgesehene Installation ist die Komponente next/script in deinem Root-Layout. Für Server Actions, Streaming SSR und die Content Security Policy (falls du eine konfiguriert hast) gelten außerdem eigene Muster des App Routers.

Installation in app/layout.tsx

Bearbeite app/layout.tsx und setze eine <Script>-Komponente ans Ende von <body>:

import Script from "next/script";

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body>
        {children}
        <Script
          id="rekomi"
          src="https://api.rekomi.com/api/v1/r/loader.js"
          strategy="afterInteractive"
          data-program-id={process.env.NEXT_PUBLIC_REKOMI_PROGRAM_ID}
          data-rkmi-email-capture="auto"
        />
      </body>
    </html>
  );
}

strategy="afterInteractive" ist die richtige Wahl für die Klickerfassung: Das Skript lädt nach der Hydration, aber bevor der Nutzer untätig wird, sodass die Empfehlung erfasst ist, bevor der Besucher auf einen CTA klickt. strategy="lazyOnload" würde zu lange warten; strategy="beforeInteractive" würde den kritischen Rendering-Pfad blockieren.

Rekomi erfasst eine E-Mail-Adresse, die ein vermittelter Besucher auf den Seiten, auf denen das Tag läuft, in ein beliebiges E-Mail- oder Textfeld tippt (Registrierung, Login, Checkout und andere Formulare), damit der Verkauf auch dann gutgeschrieben werden kann, wenn dein Checkout keine Empfehlung übergibt. Ohne Empfehlung wird nichts erfasst. Entferne das Attribut data-rkmi-email-capture, um das auszuschalten; explizite Aufrufe von Rekomi.convert() funktionieren weiterhin.

Setze NEXT_PUBLIC_REKOMI_PROGRAM_ID in deiner .env.local (und für die Produktion in den Umgebungsvariablen von Vercel oder Netlify). Das Präfix NEXT_PUBLIC_ ist nötig, damit Next die Variable im Browser-Bundle bereitstellt.

Den Verkauf erfassen

Das Browser-Pixel erfasst keine Verkäufe mehr, deshalb gibt es keinen Convert-Aufruf, den du aus einer Client-Komponente auf der Erfolgsroute auslösen müsstest. Ein Verkauf wird über einen dieser Wege erfasst:

  • Dein Zahlungsanbieter. Verbinde ihn einmal im Schritt Zahlungsanbieter verbinden unter /dashboard/setup/connect-sales (Stripe, Paddle, Braintree, Shopify, Wix, BigCommerce, Lemon Squeezy, Chargebee, Polar, Recurly, Gumroad, Creem, Dodo Payments oder Mollie), und Verkäufe, Erstattungen und Kündigungen werden automatisch erfasst.
  • Server-to-Server. Sende den Verkauf aus einem Route Handler oder einer Server Action per POST mit HMAC-Signatur an den S2S-Endpunkt von Rekomi. Siehe Server-zu-Server-Tracking.
  • Gutscheincodes. Gib jedem Affiliate einen eigenen Rabattcode (Kampagne > Gutscheincodes); ein eingelöster Code schreibt ihm den Verkauf im Checkout gut.

Übergib die Empfehlung mit window.Rekomi.getReferral() aus einer Client-Komponente an den Checkout.

Server Actions: den Affiliate-Slug vom Client übergeben

Der Rekomi-Loader speichert die Empfehlung in localStorage auf deiner Domain, nicht in einem Cookie, das der Server lesen kann. Eine Server Action kann sie also nicht aus den Cookies der Anfrage lesen. Lies den Slug stattdessen in einer Client-Komponente mit window.Rekomi.getReferral() aus und übergib ihn der Server Action als Argument. Deine Action schreibt ihn dann als rekomi_affiliate_slug in die Metadaten des Abonnements, und genau dort liest Rekomi ihn für die Attribution:

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

import Stripe from "stripe";

const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);

export async function createCheckoutSession(priceId: string, referral?: string) {
  const session = await stripe.checkout.sessions.create({
    mode: "subscription",
    line_items: [{ price: priceId, quantity: 1 }],
    success_url: `${process.env.NEXT_PUBLIC_APP_URL}/success?session_id={CHECKOUT_SESSION_ID}`,
    cancel_url: `${process.env.NEXT_PUBLIC_APP_URL}/pricing`,
    subscription_data: referral
      ? { metadata: { rekomi_affiliate_slug: referral } }
      : undefined,
  });

  return session.url;
}
// a client component that triggers checkout
"use client";

import { createCheckoutSession } from "./actions";

export function BuyButton({ priceId }: { priceId: string }) {
  async function checkout() {
    const referral = window.Rekomi?.getReferral?.() ?? undefined;
    const url = await createCheckoutSession(priceId, referral);
    if (url) window.location.href = url;
  }

  return <button onClick={checkout}>Subscribe</button>;
}

Die Empfehlung als ausdrückliches Argument zu übergeben (oder als HTTP-Header an einen Route Handler) ist das richtige Muster; es gibt kein Request-Cookie rekomi_via, das sich serverseitig lesen ließe.

Leads erfassen (optional)

window.Rekomi.convert() ist jetzt ein Aufruf zur LEAD-Erfassung, kein Verkauf. Auf einer Anmelderoute kannst du eine Kunden-E-Mail-Adresse vor der Zahlung mit der Empfehlung verknüpfen:

"use client";

import { useEffect } from "react";

export default function SignupThanks({ email }: { email: string }) {
  useEffect(() => {
    if (email) window.Rekomi?.ready?.(() => window.Rekomi.convert(email));
  }, [email]);

  return <div>Thanks for signing up!</div>;
}

Die Lead-Erfassung im Browser funktioniert in jedem Plan; der serverseitige Lead-Endpunkt erfordert Starter oder höher. Siehe Leads und Anmeldungen tracken.

Content Security Policy

Hast du in den Headern von next.config.ts eine strenge CSP konfiguriert, erlaube die Skriptquelle und die Tracking-Endpunkte von Rekomi:

// next.config.ts
const ContentSecurityPolicy = `
  default-src 'self';
  script-src 'self' https://api.rekomi.com;
  connect-src 'self' https://api.rekomi.com;
  img-src 'self' https://api.rekomi.com data:;
`;

export default {
  async headers() {
    return [{
      source: "/(.*)",
      headers: [{
        key: "Content-Security-Policy",
        value: ContentSecurityPolicy.replace(/\s{2,}/g, " ").trim(),
      }],
    }];
  },
};

Ohne Freigabe in script-src blockiert der Browser den Loader stillschweigend. Ohne connect-src schlagen die POST-Anfragen für Klicks fehl.

Middleware: meist unnötig

Die Klickerfassung läuft komplett im Client (der Loader läuft nach der Hydration, speichert die Empfehlung in localStorage und stellt window.Rekomi bereit). Rekomi-Logik in middleware.ts brauchst du nur, wenn du vor dem Rendern der Seite serverseitig etwas verarbeiten willst. Für übliche SaaS-Abläufe reicht die Erfassung im Client.

Gut zu wissen

Setze kein rohes <script> in metadata.other. Der App Router entfernt rohe Script-Tags aus den Metadaten aus Sicherheitsgründen. Nutze stattdessen next/script in app/layout.tsx.

Metadaten in Client-Komponenten werden ignoriert. Next 16 beachtet Metadata-Exporte nur aus Server-Komponenten. app/layout.tsx ist standardmäßig eine Server-Komponente; lass es dabei.

strategy="afterInteractive" ist die richtige Wahl. Andere Strategien (beforeInteractive, worker, lazyOnload) haben andere Vor- und Nachteile. After-Interactive passt für Analytics zur Klickerfassung, weil das Skript lädt, sobald die Seite nutzbar ist, aber bevor sie untätig wird.

Die Empfehlung gibt es nur im Client. Weil der Loader in localStorage schreibt, liest du sie in einer Client-Komponente mit window.Rekomi.getReferral() aus und übergibst sie an Server Actions oder Route Handler. Ein serverseitig lesbares Cookie rekomi_via gibt es nicht.

Fehlerbehebung

Das Script-Tag steht im HTML, aber der Loader läuft nicht. Die CSP blockiert ihn. Prüfe die Browser-Konsole auf CSP-Verstöße und füge api.rekomi.com zu script-src hinzu.

rekomi_affiliate_slug fehlt in den Metadaten des Abonnements. Die Empfehlung hat deine Server Action nicht erreicht. Prüfe, ob die Client-Komponente window.Rekomi.getReferral() ausliest und als Argument übergibt und ob der Loader auf der Landingpage eine Empfehlung erfasst hat (prüfe getReferral() in der Konsole).

useSearchParams liefert in der Produktion null, lokal aber nicht. Der App Router verlangt, dass useSearchParams für die statische Optimierung in <Suspense> eingebettet ist. Umschließe den Inhalt der verwendenden Komponente mit <Suspense fallback={null}>, falls du darauf stößt.

Hydration-Mismatch-Fehler nach dem Hinzufügen von <Script>. Achte darauf, dass <Script> nicht außerhalb von <html> oder <body> steht. Die Komponente gehört in <body> (meist ans Ende, damit sie das Rendern nicht blockiert).