Next.js App Router
Track referral clicks, leads, and sales in a Next.js App Router project.
Prerequisites
- Next.js 14+ with the App Router
- A Refport account and API key (Settings → API Keys)
- A referral program created in the dashboard
Install the React SDK and add <RefportTracker /> to your root layout. It reads the refp_id query parameter and persists it in a cookie automatically.
npm install @refport/reactimport { RefportTracker } from '@refport/react';
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
<RefportTracker />
{children}
</body>
</html>
);
}Install the Node.js SDK and create a shared singleton so the client is initialised once.
npm install refportimport { Refport } from 'refport';
export const refport = new Refport({
apiKey: process.env.REFPORT_API_KEY!,
});Add REFPORT_API_KEY to your .env.local.
Read the refp_id cookie with cookies() from next/headers and call track.lead() when a user signs up.
'use server';
import { cookies } from 'next/headers';
import { refport } from '@/lib/refport';
export async function signUpAction(formData: FormData) {
const user = await createUser({
email: formData.get('email') as string,
password: formData.get('password') as string,
});
try {
const cookieStore = await cookies();
const clickId = cookieStore.get('refp_id')?.value;
if (clickId) {
await refport.track.lead({
clickId,
eventName: 'Sign Up',
customerExternalId: user.id,
customerEmail: user.email,
customerName: user.name,
});
}
} catch {
/* tracking failure must never block sign-up */
}
return user;
}Using Better Auth? The @refport/better-auth plugin does this automatically — skip this step.
When a payment is completed, read the click ID from the request and call track.sale().
import { type NextRequest, NextResponse } from 'next/server';
import { getClickIdFromRequest } from 'refport';
import { refport } from '@/lib/refport';
export async function POST(request: NextRequest) {
const order = await request.json();
try {
const clickId = getClickIdFromRequest(request);
if (clickId) {
await refport.track.sale({
clickId,
customerExternalId: order.userId,
amount: order.amountCents,
currency: 'usd',
eventName: 'Purchase',
invoiceId: order.invoiceId,
paymentProcessor: 'stripe',
});
}
} catch {
/* tracking failure must never block order processing */
}
return NextResponse.json({ ok: true });
}If you create Stripe Checkout sessions in your own code, pass the click ID and customer ID in the session metadata so Refport's webhook handler can attribute the payment to the referral.
import { type NextRequest, NextResponse } from 'next/server';
import { getClickIdFromRequest } from 'refport';
import Stripe from 'stripe';
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
export async function POST(request: NextRequest) {
const clickId = getClickIdFromRequest(request);
const session = await stripe.checkout.sessions.create({
mode: 'subscription',
line_items: [{ price: 'price_xxx', quantity: 1 }],
success_url: 'https://yoursite.com/success',
cancel_url: 'https://yoursite.com/pricing',
metadata: {
refportExternalCustomerId: currentUser.id,
refportClickId: clickId ?? undefined,
},
});
return NextResponse.json({ url: session.url });
}See the Stripe integration guide for the full attribution fallback chain and alternative approaches.