Visitors Provider
Lightweight, privacy-friendly web analytics with Stripe revenue attribution
Visitors.now is a lightweight, privacy-focused web analytics platform with built-in Stripe revenue attribution.
Installation
No npm package required. VisitorsClientProvider loads the tracking script directly from the Visitors CDN at runtime.
Client-Side Usage
import { createClientAnalytics } from '@stacksee/analytics/client';
import { VisitorsClientProvider } from '@stacksee/analytics/providers/client';
const analytics = createClientAnalytics({
providers: [
new VisitorsClientProvider({
token: process.env.NEXT_PUBLIC_VISITORS_TOKEN!,
persist: true // sets data-persist on the script tag
})
]
});
await analytics.initialize();Configuration Options
| Option | Type | Required | Description |
|---|---|---|---|
token | string | Yes | Your Visitors project token from the dashboard |
persist | boolean | No | Enable persist mode — sets data-persist on the script tag so a visitor cookie is written. Required for cross-session tracking and Stripe revenue attribution (default: false) |
debug | boolean | No | Enable debug logging (default: false) |
enabled | boolean | No | Disable the provider entirely (default: true) |
Tracking Events
Page Views
Page views are tracked automatically when the Visitors script loads. Calling analytics.pageView() is optional and results in a debug log only — no duplicate event is sent.
Custom Events
analytics.track('button_clicked', {
button: 'cta',
page: 'pricing',
value: 99
});Property values must be string or number. Object and array values are silently dropped to match the Visitors API contract.
Identify Users
analytics.identify('user-123', {
email: 'user@example.com',
name: 'Jane Smith',
plan: 'pro',
seats: 5
});Calls visitors.identify({ id, ...traits }). Non-scalar trait values are filtered out automatically.
Identification requires persist: true in your VisitorsClientProvider config.
EU users must give explicit consent before calling identify().
Page Leave
analytics.pageLeave({ section: 'hero' });Sends a page_leave custom event via visitors.track().
Stripe Revenue Attribution
Visitors.now can attribute Stripe revenue to individual visitors through a fallback chain: checkout metadata → previous attribution → email matching.
How It Works
- The Visitors script sets a
visitorcookie in the browser (requires persist mode) - You read the cookie value and pass it in the Stripe checkout session metadata
- Visitors.now matches the purchase to the visitor session
Implementation
Step 1 — Identify users when they sign in (required for email-based fallback):
analytics.identify('user-123', {
email: 'user@example.com',
name: 'Jane Smith'
});Step 2 — Read the visitor cookie on the client and send it to your server:
// Get the visitor ID for Stripe attribution
const visitorId = visitorsProvider.getVisitorId();
// Send to your server when creating a checkout session
const response = await fetch('/api/create-checkout', {
method: 'POST',
body: JSON.stringify({ visitorId, priceId: 'price_xxx' })
});Step 3 — Pass the visitor ID in your Stripe checkout session (server-side):
// pages/api/create-checkout.ts (Next.js example)
import Stripe from 'stripe';
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
export async function POST(req: Request) {
const { visitorId, priceId } = await req.json();
const session = await stripe.checkout.sessions.create({
mode: 'subscription',
line_items: [{ price: priceId, quantity: 1 }],
success_url: `${process.env.NEXT_PUBLIC_URL}/success`,
cancel_url: `${process.env.NEXT_PUBLIC_URL}/pricing`,
// Pass the visitor cookie for revenue attribution
metadata: {
visitor: visitorId ?? ''
}
});
return Response.json({ url: session.url });
}Revenue attribution requires:
persist: trueset inVisitorsClientProviderconfig (addsdata-persistto the script tag so thevisitorcookie is written)- A Stripe API key connected under Settings → Integrations
visitors.identify()called when users sign in (for email-based fallback)
Framework Examples
Next.js App Router
// lib/analytics.ts
import { createClientAnalytics } from '@stacksee/analytics/client';
import { VisitorsClientProvider } from '@stacksee/analytics/providers/client';
export const visitorsProvider = new VisitorsClientProvider({
token: process.env.NEXT_PUBLIC_VISITORS_TOKEN!,
persist: true,
debug: process.env.NODE_ENV === 'development'
});
export const analytics = createClientAnalytics({
providers: [visitorsProvider]
});// app/layout.tsx
'use client';
import { useEffect } from 'react';
import { usePathname } from 'next/navigation';
import { analytics } from '@/lib/analytics';
export default function RootLayout({ children }: { children: React.ReactNode }) {
const pathname = usePathname();
useEffect(() => {
analytics.initialize();
}, []);
useEffect(() => {
analytics.pageView({ path: pathname });
}, [pathname]);
return <html><body>{children}</body></html>;
}Provider Behaviour Reference
| Method | Behaviour |
|---|---|
initialize() | Injects cdn.visitors.now/v.js script; idempotent |
track(event, ctx) | Calls visitors.track(action, scalarProps) |
identify(id, traits) | Calls visitors.identify({ id, ...scalarTraits }) |
pageView() | No-op — page views are automatic |
pageLeave() | Calls visitors.track('page_leave', ...) |
reset() | No-op — Visitors has no native reset method |
getVisitorId() | Reads the visitor cookie for Stripe attribution |
Privacy Features
- No cookies by default — opt in via
persist: true - GDPR-compliant —
identify()requires explicit EU consent when persist mode is enabled - Bot filtering built-in