How to set up Next.js app router analytics, feature flags, and more
May 22, 2025
Next.js is one of the most popular frameworks for building web apps. Need to know what users are doing in these apps or release a feature safely? PostHog can help.
In this tutorial, we'll create a simple Next.js app and set up PostHog on the client and server side. We'll also capture pageviews and custom events, set up feature flags, and more.
If you use Next.js with the pages router, check out our other Next.js pages router analytics tutorial.
Creating a Next.js app with the app router
First, once Node is installed, create a Next.js app. Select No for TypeScript, Yes for use app router
, and the defaults for every other option.
npx create-next-app@latest next-app
We name our app next-app
and can go into the newly created folder to run it.
cd next-appnpm run dev
This opens a new page showing we are running Next.js.
Next, we'll add a couple pages to the app. In the app folder, create a new folder named about
and create a page.js
file inside with a basic component.
// app/about/page.jsimport Link from 'next/link'export default function About() {return (<main><h1>About</h1><Link href="/">Go home</Link></main>)}
Change our app page.js
file to a title and a link to the new about
page.
// app/page.jsimport styles from './page.module.css'import Link from 'next/link'export default function Home() {return (<main className={styles.main}><h1>Home</h1><Link href="/about">Go to About</Link></main>)}
You can now move between the home and about pages which will be useful for testing event capture next.
Setting up PostHog on the client side
First, we need a PostHog instance (signup for free). From this instance, we need a project API key, which is in project settings, and an the relevant ingestion address (http://us.i.posthog.com
, http://eu.i.posthog.com
, or a custom address). Add both of these to a .env.local
file in our base directory.
NEXT_PUBLIC_POSTHOG_KEY=<ph_project_api_key>NEXT_PUBLIC_POSTHOG_HOST=https://us.i.posthog.com
Using the Next.js app router requires us to initialize PostHog differently than with the pages router. Specifically, the app router server-side renders components by default, and the posthog-js
library is a client-side library.
To make these work together, create a providers.js
file and set up the PostHogProvider
with the 'use client'
directive.
// app/providers.js'use client'import posthog from 'posthog-js'import { PostHogProvider } from 'posthog-js/react'import { useEffect } from 'react'export function PHProvider({ children }) {useEffect(() => {posthog.init(process.env.NEXT_PUBLIC_POSTHOG_KEY, {api_host: process.env.NEXT_PUBLIC_POSTHOG_HOST,defaults: '2025-05-24',})}, []);return <PostHogProvider client={posthog}>{children}</PostHogProvider>}
Once we do this, we can then import the provider.js
file in our app/layout.js
file, and wrap our app in the PostHog provider.
// app/layout.jsimport './globals.css'import { PHProvider } from './providers'export default function RootLayout({ children }) {return (<html lang="en"><PHProvider><body>{children}</body></PHProvider></html>)}
After you set this up, PostHog starts autocapturing events and pageviews. This also means you can use the PostHog on the client side in all your client-side rendered Next.js components (the ones with the "use client"
directive).


Note: If you don’t use the
"use client"
directive, Next.js assumes your page is server-side rendered. This means the client hooks likeusePostHog
cause errors. To interact with PostHog on the server side, use the PostHog Node SDK (which we show later).
Using PostHog with server-rendered components
Server-rendered components are the default for the app router. getServerProps
from the pages router is not used anymore. This means if we want server-side rendered feature flags or get other data from PostHog, we must use the PostHog Node SDK.
To set this up, create a posthog.js
file in the app folder that returns a PostHog Node client:
// app/posthog.jsimport { PostHog } from 'posthog-node'export default function PostHogClient() {const posthogClient = new PostHog(process.env.NEXT_PUBLIC_POSTHOG_KEY, {host: process.env.NEXT_PUBLIC_POSTHOG_HOST,})return posthogClient}
We can then use this function to get access to the Node client in our server components.
Capturing custom events
As a basic example of what you can do on the server side, we can capture custom events as long as we have a distinctId
. To do this, get the PostHogClient
from the posthog.js
file, then call posthog.capture()
like this:
// app/about/page.jsimport Link from 'next/link'import PostHogClient from '../posthog'export default function About() {const posthog = PostHogClient()posthog.capture({distinctId: 'ian@posthog.com', // replace with a user's distinct IDevent: 'server_side_event_name'})return (<main><h1>About</h1><Link href="/">Go home</Link></main>)}
Setting up feature flags
We can create a server-side function along with the PostHog Node client to get data about a user’s feature flags, then use that data to conditionally render a part of the component.
To do this, create a feature flag in PostHog with the key main-cta
, roll it out to 100% of users, and then add the code to check it in a function (in the example below, we name it getData()
). Because you are awaiting the posthog request now, make sure to add async
to the main About()
function as well.
The feature flag does require a distinct user ID, which we hardcoded for now, but you could also set up authentication as we’ve shown in the Next.js analytics tutorial or cookies (using the Cookies function) as we showed in the Next.js A/B test tutorial.
import Link from 'next/link'import PostHogClient from '../posthog'export default async function About() {const flags = await getData();return (<main><h1>About</h1><Link href="/">Go home</Link>{ flags['main-cta'] &&<Link href="http://posthog.com/">Go to PostHog</Link>}</main>)}async function getData() {const posthog = PostHogClient()const flags = await posthog.getAllFlags('ian@posthog.com' // replace with a user's distinct ID);return flags}
With this, you have the basics of PostHog set up on both the client and server side with Next.js and the app router.
Further reading
- How to set up Next.js analytics, feature flags, and more
- How to set up Next.js A/B tests
- An introductory guide to identifying users in PostHog
Subscribe to our newsletter
Product for Engineers
Read by 60,000+ founders and builders
We'll share your email with Substack
Questions? Ask Max AI.
It's easier than reading through 635 pages of documentation