Next.js Integration Guide
This guide covers how to integrate Pulsora Analytics with Next.js applications, supporting both the App Router (Next.js 13+) and Pages Router.
Installation
First, install the required packages:
npm install @pulsora/core @pulsora/react
App Router Setup (Next.js 13+)
1. Create Analytics Provider
Create a client component for analytics:
// app/components/analytics-provider.tsx
'use client';
import { PulsoraProvider } from '@pulsora/react';
import { usePathname } from 'next/navigation';
import { usePageview } from '@pulsora/react';
import { useEffect } from 'react';
function PageviewTracker() {
const pathname = usePathname();
// Track pageviews on route change
usePageview({ trigger: pathname });
return null;
}
export function AnalyticsProvider({
children
}: {
children: React.ReactNode
}) {
return (
<PulsoraProvider
config={{
apiToken: process.env.NEXT_PUBLIC_PULSORA_TOKEN!,
debug: process.env.NODE_ENV === 'development'
}}
>
<PageviewTracker />
{children}
</PulsoraProvider>
);
}
2. Add to Root Layout
Wrap your app with the analytics provider:
// app/layout.tsx
import { AnalyticsProvider } from '@/components/analytics-provider';
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body>
<AnalyticsProvider>
{children}
</AnalyticsProvider>
</body>
</html>
);
}
3. Environment Variables
Add your Pulsora API token to .env.local:
NEXT_PUBLIC_PULSORA_TOKEN=your-api-token-here
Pages Router Setup
1. Custom App Component
For Pages Router, modify _app.tsx:
// pages/_app.tsx
import { PulsoraProvider, usePageview } from '@pulsora/react';
import { useRouter } from 'next/router';
import type { AppProps } from 'next/app';
function PageviewTracker() {
const router = useRouter();
// Track pageviews on route change
usePageview({ trigger: router.asPath });
return null;
}
export default function MyApp({ Component, pageProps }: AppProps) {
return (
<PulsoraProvider
config={{
apiToken: process.env.NEXT_PUBLIC_PULSORA_TOKEN!,
debug: process.env.NODE_ENV === 'development'
}}
>
<PageviewTracker />
<Component {...pageProps} />
</PulsoraProvider>
);
}
Tracking Events
Using Hooks
Track custom events in your components:
// components/signup-form.tsx
import { useEvent } from '@pulsora/react';
export function SignupForm() {
const trackEvent = useEvent();
const handleSubmit = async (e: FormEvent) => {
e.preventDefault();
// Track form submission
trackEvent('signup_form_submit', {
location: 'header',
variant: 'modal'
});
// Your signup logic...
};
const handleFieldFocus = (fieldName: string) => {
trackEvent('form_field_focus', {
form: 'signup',
field: fieldName
});
};
return (
<form onSubmit={handleSubmit}>
<input
name="email"
type="email"
onFocus={() => handleFieldFocus('email')}
placeholder="Email"
/>
<button type="submit">Sign Up</button>
</form>
);
}
Server Components
For server components, create a client wrapper:
// components/track-event-button.tsx
'use client';
import { useEvent } from '@pulsora/react';
export function TrackEventButton({
eventName,
eventData,
children,
...props
}: {
eventName: string;
eventData?: Record<string, any>;
children: React.ReactNode;
[key: string]: any;
}) {
const trackEvent = useEvent();
return (
<button
{...props}
onClick={(e) => {
trackEvent(eventName, eventData);
props.onClick?.(e);
}}
>
{children}
</button>
);
}
Use in server components:
// app/products/page.tsx
import { TrackEventButton } from '@/components/track-event-button';
export default function ProductsPage() {
return (
<div>
<h1>Our Products</h1>
<TrackEventButton
eventName="product_view"
eventData={{ product: 'premium-plan' }}
className="btn-primary"
>
View Premium Plan
</TrackEventButton>
</div>
);
}
Revenue Tracking
Checkout Integration
Pass tracking data to your payment processor:
// app/api/checkout/route.ts
import { NextRequest } from 'next/server';
import Stripe from 'stripe';
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
export async function POST(request: NextRequest) {
const { priceId, visitor_fingerprint, session_id } = await request.json();
const session = await stripe.checkout.sessions.create({
payment_method_types: ['card'],
line_items: [
{
price: priceId,
quantity: 1,
},
],
mode: 'payment',
success_url: `${process.env.NEXT_PUBLIC_APP_URL}/success`,
cancel_url: `${process.env.NEXT_PUBLIC_APP_URL}/cancel`,
metadata: {
visitor_fingerprint,
session_id,
},
});
return Response.json({ sessionId: session.id });
}
Client-Side Checkout
// components/checkout-button.tsx
'use client';
import { usePulsora } from '@pulsora/react';
import { loadStripe } from '@stripe/stripe-js';
export function CheckoutButton({ priceId }: { priceId: string }) {
const pulsora = usePulsora();
const handleCheckout = async () => {
// Get tracking data
const fingerprint = await pulsora.getVisitorFingerprint();
const sessionId = pulsora.getSessionId();
// Create checkout session
const response = await fetch('/api/checkout', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
priceId,
visitor_fingerprint: fingerprint,
session_id: sessionId
})
});
const { sessionId: stripeSessionId } = await response.json();
// Redirect to Stripe
const stripe = await loadStripe(process.env.NEXT_PUBLIC_STRIPE_KEY!);
await stripe!.redirectToCheckout({ sessionId: stripeSessionId });
};
return (
<button onClick={handleCheckout}>
Subscribe Now
</button>
);
}
API Routes
Track Events Server-Side
For server-side event tracking:
// app/api/track/route.ts
import { Pulsora } from '@pulsora/core';
import { NextRequest } from 'next/server';
// Initialize server-side tracker
const pulsora = new Pulsora();
pulsora.init({
apiToken: process.env.PULSORA_SERVER_TOKEN!,
});
export async function POST(request: NextRequest) {
const { eventName, eventData, visitorFingerprint, sessionId } =
await request.json();
// Track event with visitor context
await pulsora.event(eventName, {
...eventData,
visitor_fingerprint: visitorFingerprint,
session_id: sessionId,
});
return Response.json({ success: true });
}
Middleware Integration
Track server-side events using Next.js middleware:
// middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
export function middleware(request: NextRequest) {
const response = NextResponse.next();
// Track API usage
if (request.nextUrl.pathname.startsWith('/api/')) {
// Log API access (implement your tracking logic)
console.log('API accessed:', {
path: request.nextUrl.pathname,
method: request.method,
timestamp: new Date().toISOString(),
});
}
return response;
}
export const config = {
matcher: '/api/:path*',
};
Static Site Generation (SSG)
For static pages, track pageviews client-side only:
// pages/blog/[slug].tsx (Pages Router)
import { usePageview } from '@pulsora/react';
export default function BlogPost({ post }: { post: Post }) {
// Track pageview on client-side mount
usePageview();
return (
<article>
<h1>{post.title}</h1>
<div>{post.content}</div>
</article>
);
}
export async function getStaticProps({ params }) {
// Fetch post data
const post = await getPost(params.slug);
return {
props: { post },
revalidate: 3600 // ISR: revalidate every hour
};
}
Performance Optimization
Dynamic Import
Load Pulsora only in the browser:
// app/components/dynamic-analytics.tsx
import dynamic from 'next/dynamic';
const AnalyticsProvider = dynamic(
() => import('./analytics-provider').then(mod => mod.AnalyticsProvider),
{ ssr: false }
);
export default function Layout({ children }: { children: React.ReactNode }) {
return (
<AnalyticsProvider>
{children}
</AnalyticsProvider>
);
}
Conditional Loading
Load analytics based on user consent:
// app/components/conditional-analytics.tsx
'use client';
import { useState, useEffect } from 'react';
import { PulsoraProvider } from '@pulsora/react';
export function ConditionalAnalytics({
children
}: {
children: React.ReactNode
}) {
const [hasConsent, setHasConsent] = useState(false);
useEffect(() => {
// Check for user consent
const consent = localStorage.getItem('analytics-consent');
setHasConsent(consent === 'true');
}, []);
if (!hasConsent) {
return <>{children}</>;
}
return (
<PulsoraProvider
config={{
apiToken: process.env.NEXT_PUBLIC_PULSORA_TOKEN!
}}
>
{children}
</PulsoraProvider>
);
}
Debugging
Enable Debug Mode
Set debug mode in development:
// app/components/analytics-provider.tsx
<PulsoraProvider
config={{
apiToken: process.env.NEXT_PUBLIC_PULSORA_TOKEN!,
debug: process.env.NODE_ENV === 'development',
endpoint: process.env.NEXT_PUBLIC_PULSORA_ENDPOINT // Optional custom endpoint
}}
>
Verify Installation
Check the browser console for debug logs:
[Pulsora] Initialized with token: pub_***
[Pulsora] Pageview tracked: /products
[Pulsora] Event tracked: button_click { location: 'header' }
Common Issues
Hydration Mismatch
If you encounter hydration errors, ensure analytics code runs only on the client:
// Use dynamic import with ssr: false
const Analytics = dynamic(() => import('./analytics'), { ssr: false });
Missing Environment Variables
Ensure your .env.local file contains:
NEXT_PUBLIC_PULSORA_TOKEN=your-public-api-token
For server-side tracking, also add:
PULSORA_SERVER_TOKEN=your-server-api-token
Route Changes Not Tracked
Ensure you're using the correct router hook:
- App Router:
usePathname()fromnext/navigation - Pages Router:
useRouter()fromnext/router
Next Steps
- Set up revenue tracking for your payment flow
- Configure custom events for your use case
- Review best practices for optimal tracking