PostHog Setup Guide
PostHog is an open-source analytics platform that allows you to track user behavior, create funnels, analyze feature usage, and more. This guide walks you through adding PostHog to a Next.js application, using client-side tracking for pageviews and custom events.
1. Prerequisites
- A PostHog account and project set up at PostHog.
- A Next.js (13+) application using the new App Router.
- Basic familiarity with Next.js client/server components.
2. Environment Variables
To begin, you need two environment variables:
NEXT_PUBLIC_POSTHOG_KEY: Your PostHog project’s public API key (also called a “client key”).NEXT_PUBLIC_POSTHOG_HOST: The URL where PostHog is hosted. For example:https://app.posthog.com(if you’re using PostHog Cloud)https://us.i.posthog.com(US-based PostHog instance)- Your self-hosted PostHog domain (e.g.,
https://analytics.mydomain.com)
Add them to .env.local (or wherever your local environment variables are stored):
NEXT_PUBLIC_POSTHOG_KEY=your_posthog_keyNEXT_PUBLIC_POSTHOG_HOST=https://us.i.posthog.comNote: Since these variables are public, they must start with
NEXT_PUBLIC_. If you need to store private or secret keys, consider using server-side environments instead.
3. Provider Setup
Next, you’ll create a PostHog provider that initializes the PostHog client on the client side.
Create a file, e.g. app/providers.tsx:
'use client';import React from 'react';import posthog from 'posthog-js';import { PostHogProvider } from 'posthog-js/react';
// Initialize PostHog client in the browser onlyif (typeof window !== 'undefined') { posthog.init(process.env.NEXT_PUBLIC_POSTHOG_KEY!, { api_host: process.env.NEXT_PUBLIC_POSTHOG_HOST!, // Only create profiles for identified users person_properties: 'identified_only', capture_pageview: false, // We'll capture pageviews manually });}
export function CSPostHogProvider({ children }: { children: React.ReactNode }) { return <PostHogProvider client={posthog}>{children}</PostHogProvider>;}Key Options
capture_pageview: false: Prevents automatic pageview tracking; we’ll handle it manually for finer control.person_properties: 'identified_only': Ensures profiles are created only if a user is identified (this can help you respect user privacy settings or anonymity preferences).
4. Pageview Tracking
To manually track pageviews, create a client component that listens for route changes. For instance, app/pageview.tsx:
'use client';import { usePathname, useSearchParams } from 'next/navigation';import { useEffect } from 'react';import { usePostHog } from 'posthog-js/react';
export default function PostHogPageView() { const pathname = usePathname(); const searchParams = useSearchParams(); const posthog = usePostHog();
useEffect(() => { if (pathname && posthog) { let url = window.origin + pathname; if (searchParams.toString()) { url += `?${searchParams.toString()}`; }
// Capture pageview event posthog.capture('$pageview', { $current_url: url, }); } }, [pathname, searchParams, posthog]);
return null;}Explanation
usePathname()&useSearchParams(): Hooks from Next.js to detect route changes.usePostHog(): Hook fromposthog-js/reactto access the PostHog client.posthog.capture('$pageview', { ... }): Manually track a$pageviewevent with a custom URL.
Note:
$pageviewis a special event name recognized by PostHog’s UI. You could also name your event something else, likemy_pageview, if you prefer.
5. Root Layout Integration
Finally, integrate both the PostHog provider and pageview tracking component into your root layout (e.g. app/layout.tsx):
import React from 'react';import { CSPostHogProvider } from './providers';import dynamic from 'next/dynamic';
const PostHogPageView = dynamic(() => import('./pageview'), { ssr: false, // This ensures it's only rendered on the client});
export default function RootLayout({ children,}: { children: React.ReactNode;}) { return ( <html lang="en"> <CSPostHogProvider> <body> <PostHogPageView /> {children} </body> </CSPostHogProvider> </html> );}dynamic(..., { ssr: false }): Disables server-side rendering, ensuring our analytics code only runs in the browser.
6. Usage & Custom Events
With PostHog integrated, you can track custom events anywhere in your client-side code. For example, in a React component:
'use client';import { usePostHog } from 'posthog-js/react';
export function MyComponent() { const posthog = usePostHog();
const handleButtonClick = () => { posthog?.capture('button_clicked', { buttonName: 'exampleButton', // ...any other custom properties }); };
return ( <button onClick={handleButtonClick}>Click Me</button> );}Tips for Custom Events
- Use clear, descriptive event names (e.g.
button_clicked,purchase_completed,user_signed_out). - Pass any relevant event properties in an object (e.g., user ID, button label, feature flags).
- Consider using PostHog’s Feature Flags to roll out new UI changes to a subset of users.
7. Additional Tips
- Identify Users: If you have a user authentication system, consider using
posthog.identify(user.id)to tie events to a specific user. - Session Recording: Enable session recording in your PostHog settings if you want to replay user sessions.
- Compliance & Privacy: Make sure to follow GDPR/CCPA or other privacy regulations when collecting user data.
- Production vs. Staging: Use separate PostHog projects or environment variables for staging vs. production to keep data clean.
Conclusion
You’ve successfully integrated PostHog into your Next.js application. By manually capturing pageviews and custom events, you have fine-grained control over what analytics data is collected. Explore PostHog’s documentation for more features like funnels, heatmaps, session recording, and more advanced user behavior analytics.
Happy tracking!