If you're building with Next.js and want deep, actionable insights into how users interact with your app — PostHog is a game changer.
PostHog is an open-source analytics platform that helps teams track events, monitor user behavior, and make data-driven product decisions. And the best part? It works beautifully with modern frameworks like Next.js — including support for client-side navigation.
In this post, I’ll show you how to set up a production-ready PostHog integration in your Next.js project. We’ll cover:
Initializing PostHog (only in production)
Setting up a PostHog Provider
Identifying logged-in users
Manually tracking pageviews
Avoiding ad blockers with proxy rewrites
First, add the necessary packages:
bashCopyEdit
npm install posthog-js @posthog/react # or with pnpm pnpm add posthog-js @posthog/react
We’ll create a custom provider to initialize PostHog and wrap our app in context. This ensures PostHog runs only in production and allows us to track data globally.
tsxCopyEdit
// src/app/_providers/posthogProvider.tsx "use client"; import { useEffect, Suspense } from "react"; import { PostHogProvider as PHProvider, usePostHog } from "posthog-js/react"; import posthog from "posthog-js"; import { useSession } from "next-auth/react"; import { usePathname, useSearchParams } from "next/navigation"; export function PostHogProvider({ children }: { children: React.ReactNode }) { useEffect(() => { if (process.env.NODE_ENV !== "development") { posthog.init(process.env.NEXT_PUBLIC_POSTHOG_KEY as string, { api_host: ${process.env.NEXT_PUBLIC_BASE_URL}/ingest, ui_host: "https://us.posthog.com", capture_pageview: false, // We'll handle this manually }); } }, []); if (process.env.NODE_ENV === "development") { return <>{children}</>; } return ( <PHProvider client={posthog}> <SuspendedPostHogPageView /> {children} </PHProvider> ); }
We’ll use next-auth (or your preferred auth method) to tie events to real users using PostHog’s identify method.
tsxCopyEdit
function PostHogPageView() { const posthog = usePostHog(); const { data: session } = useSession(); useEffect(() => { if (session?.user.id) { posthog.identify(session.user.id, { email: session.user.email, }); } else { posthog.reset(); } }, [posthog, session?.user]); }
Since Next.js uses client-side navigation, automatic pageview tracking won’t work. Let’s handle it manually:
tsxCopyEdit
function PostHogPageView() { const posthog = usePostHog(); const pathname = usePathname(); const searchParams = useSearchParams(); useEffect(() => { if (pathname && posthog) { let url = window.origin + pathname; if (searchParams.toString()) { url += "?" + searchParams.toString(); } posthog.capture("$pageview", { $current_url: url }); } }, [pathname, searchParams, posthog]); return null; }
To avoid degrading your app's SSR performance, wrap the tracker in a Suspense component.
tsxCopyEdit
function SuspendedPostHogPageView() { return ( <Suspense fallback={null}> <PostHogPageView /> </Suspense> ); }
Include the PostHogProvider in your root layout so it's accessible across your app.
tsxCopyEdit
// src/app/layout.tsx import { PostHogProvider } from "./_providers/posthogProvider"; export default function RootLayout({ children }: { children: React.ReactNode }) { return <PostHogProvider>{children}</PostHogProvider>; }
Make sure to include your PostHog keys in your .env file:
envCopyEdit
NEXT_PUBLIC_POSTHOG_KEY=phc_xxx_your_key NEXT_PUBLIC_BASE_URL=https://yourdomain.com
Many users have ad blockers that block analytics domains. By proxying requests through your own domain, you reduce this risk.
Add the following to next.config.mjs:
jsCopyEdit
async rewrites() { return [ { source: "/ingest/static/:path*", destination: "https://us-assets.i.posthog.com/static/:path*", }, { source: "/ingest/:path*", destination: "https://us.i.posthog.com/:path*", }, ]; }
With this setup, you now have a clean, production-ready PostHog integration in your Next.js app:
Analytics only run in production
Logged-in users are identified
Pageviews are manually tracked on client-side routes
SSR remains optimized
This is just the beginning — you can build on this to track custom events, A/B tests, funnel analysis, and much more.
Happy tracking!