PostHog is a powerful open-source analytics platform that enables product teams to track user behavior, capture events, and gain actionable insights. Integrating PostHog with a Next.js application allows you to track authenticated users, custom events, and pageviews—even in apps with client-side navigation.
This tutorial will walk you through a production-grade integration of PostHog in a Next.js app, including:
Initializing PostHog only in production
Using the PostHog React provider
Identifying authenticated users
Manually capturing pageviews
Rewrite proxy to afford adblockers
First, install the required packages:
npm install posthog-js @posthog/react
Or with pnpm:
pnpm add posthog-js @posthog/react
Create a provider component to initialize PostHog and wrap your app.
This ensures PostHog is only initialized in production and provides the PostHog context to your app.
// 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"; // use your own provider
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 capture manually
});
}
}, []);
if (process.env.NODE_ENV === "development") {
return <>{children}</>;
}
return (
<PHProvider client={posthog}>
<SuspendedPostHogPageView />
{children}
</PHProvider>
);
}
To associate analytics data with authenticated users, use NextAuth’s session data and PostHog’s identify
method.
function PostHogPageView() {
const posthog = usePostHog();
const { data: userInfo } = useSession(); // get this from your auth provider
useEffect(() => {
if (userInfo?.user.id) {
posthog.identify(userInfo.user.id, {
email: userInfo.user.email,
});
} else {
posthog.reset();
}
}, [posthog, userInfo?.user]);
// ...
}
Next.js uses client-side navigation, so you need to manually capture pageviews on route changes.
import { usePathname, useSearchParams } from "next/navigation";
function PostHogPageView() {
// ... user identification code above
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;
}
Next.js recommends using Suspense to avoid de-optimizing your app into client-side rendering when using certain hooks.
function SuspendedPostHogPageView() {
return (
<Suspense fallback={null}>
<PostHogPageView />
</Suspense>
);
}
In your root layout wrap your app with the PostHogProvider
:
// src/app/layout.tsx
import { PostHogProvider } from "./_providers/posthogProvider";
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return <PostHogProvider>{children}</PostHogProvider>;
}
Add your PostHog project key and base URL to your environment variables:
NEXT_PUBLIC_POSTHOG_KEY=phc_xxx_your_key
NEXT_PUBLIC_BASE_URL=https://yourdomain.com
To avoid CORS issues and ad blockers and keep your analytics endpoint stable, proxy PostHog requests through your Next.js app.
In your next.config.mjs
:
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 have a robust, production-ready PostHog integration in your Next.js app:
Analytics only runs in production
Authenticated users are identified
Pageviews are tracked accurately
SSR/CSR performance is preserved
You can now leverage PostHog’s full power for product analytics, user behavior tracking, and event capture in your Next.js application.
This is a minimal setup; you can be extended for more metrics.
Note: This article was scaffolded using AI referencing my project's codebase.
Join Raymond on Peerlist!
Join amazing folks like Raymond and thousands of other people in tech.
Create ProfileJoin with Raymond’s personal invite link.
0
10
1