Raymond Asogwa

May 26, 2025 • 3 min read

Integrating PostHog Analytics with Next.js

Integrating PostHog Analytics with Next.js

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


1. Install Dependencies

First, install the required packages:

npm install posthog-js @posthog/react

Or with pnpm:

pnpm add posthog-js @posthog/react

2. Create a PostHog Provider Component

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>
  );
}

3. Identify Authenticated Users

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]);
  // ...
}

4. Manually Track Pageviews

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;
}

5. Use Suspense to Avoid De-optimizing SSR

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>
  );
}

6. Wrap Your App with the Provider

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>;
}

7. Environment Variables

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

8. Secure and Proxy PostHog API Requests

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*",
      },
    ];
  },

Conclusion

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 Profile

Join with Raymond’s personal invite link.

0

10

1