Next.js: @posthog/next
Contents
@posthog/next is currently in pre-release. The API may change before the stable release. For production apps, see the standard Next.js setup guide.
@posthog/next is a unified package for integrating PostHog into your Next.js app. It provides a simplified interface that handles common patterns out of the box:
- Synchronized analytics identity across client and server, so both sides share the same user automatically
- Trusted server-side identity resolution from your authentication session for server events and feature flag evaluations
- Server-side feature flag bootstrapping so hooks return real values immediately with no flicker
- Eager client initialization ensures PostHog is always available when your components render
- Built-in API proxy that routes SDK traffic through your domain to avoid ad blockers
- Automatic browser exception capture and event flushing with no manual
shutdown()orflush()calls needed
This guide covers both the App Router and Pages Router. You'll need a PostHog instance (Cloud or self-hosted) and a Next.js 13.0.0+ application.
- 1
Install @posthog/next
Required - 2
Add your environment variables
RequiredAdd these to your
.env.localfile and to your hosting provider (e.g. Vercel, Netlify, AWS). You can find your project token in your project settings..env.localNEXT_PUBLIC_POSTHOG_HOSTis optional and defaults tohttps://us.i.posthog.com. - 3
Add the middleware
RecommendedThe middleware seeds an identity cookie on first visit so client and server share the same user, and optionally proxies API requests through your domain.
middleware.tsSetting
proxy: trueroutes PostHog API calls through your domain at/ingest, which helps avoid ad blockers. You can customize the path prefix by passingproxy: { pathPrefix: '/analytics' }instead.Note: Middleware requires a server runtime and is not available with
output: 'export'(fully static sites). If you can't use middleware, you can set up a reverse proxy separately to route traffic through your domain. - 4
Wrap your app with PostHogProvider
RequiredAdd
PostHogProviderandPostHogPageViewto your root layout:app/layout.tsxPostHogProvideris a React Server Component. WhenbootstrapFlagsis enabled, it evaluates feature flags server-side and passes the results to the client so hooks return real values immediately.Note: Enabling
bootstrapFlagsopts the route into dynamic rendering (incompatible with static generation / ISR). Without it, the provider does not call any dynamic Next.js APIs and is safe for static pages.Add
PostHogProviderandPostHogPageViewto your_app:pages/_app.tsxNote: For the Pages Router, import from
@posthog/next/pagesinstead of@posthog/next. Verify events are captured
CheckpointConfirm that you can see events in your PostHog projectVisit your app and click around. Within a minute or two, you should see
$pageviewand autocaptured events (like clicks) appear in the activity tab.- 5
Access PostHog in client components
RequiredAll hooks must be used in client components (
'use client').Capture events:
TSXFeature Flags:
TSXConditional rendering with
PostHogFeature:TSXYou can also read the full
posthog-jsdocumentation for all the usable functions. - 6
Identify your user
RecommendedCall
posthog.identify()on the client after login to link events to a known user. This connects event captures, session replays, LLM traces, and Error Tracking to the same person across client and server.TSXSee our guide on identifying users for more details.
For server-side events and feature flag evaluations, PostHog can use the client-provided identity cookie and tracing headers to link analytics across the request. These values come from the client. When you need a strict guarantee that analytics uses the identity from your authenticated session, provide an authoritative distinct ID directly when capturing an event or evaluating feature flags, or configure
getDistinctIdincreatePostHog()to resolve it from your server session by default. - 7
Use PostHog on the server
RecommendedCreate a shared PostHog module with
createPostHog(). Use the returnedgetPostHogto evaluate feature flags in Server Components and capture events from request or action handlers.lib/posthog.tsapp/dashboard/page.tsxWithout
getDistinctId, PostHog can use the identity cookie and tracing headers supplied by the client as request context. When you need a strict guarantee that analytics uses the identity from your authenticated session, pass an authoritative distinct ID directly when capturing events or evaluating feature flags. When the resolver returns an ID, PostHog uses it as the request default ahead of client-provided identity, while an ID passed directly to a capture or flag method still takes precedence. Session and device linkage can remain connected to the browser. The resolver runs at most once per request and is not called for users who opted out of capture.Note:
getPostHog()callscookies()andheaders()internally, which opts the route into dynamic rendering. Pages using it cannot be statically generated. Keepimport 'server-only'in the shared module so accidental imports from Client Components fail at build time.Event flushing: On Vercel,
@posthog/nextauto-detectswaitUntilfrom@vercel/functionsso events are flushed after the response is sent without blocking it. No manualshutdown()call is needed. In other environments, pass a customwaitUntilin the server client options withcreatePostHog({ options: { waitUntil } }), or events will be batched and flushed normally.Create a shared PostHog module with
createPostHog(), then call the returnedgetPostHogwith the request context insidegetServerSidePropsto evaluate feature flags.lib/posthog.tspages/dashboard.tsxWithout
getDistinctId, PostHog can use the identity cookie and tracing headers supplied by the client as request context. When you need a strict guarantee that analytics uses the identity from your authenticated session, pass an authoritative distinct ID directly when capturing events or evaluating feature flags. When the resolver returns an ID, PostHog uses it as the request default ahead of client-provided identity, while an ID passed directly to a capture or flag method still takes precedence. Session and device linkage can remain connected to the browser. The resolver runs once pergetPostHog(ctx)call and is not called for users who opted out of capture.The Pages Router provider doesn't have the App Router's
bootstrapFlagsprop. For the same no-flicker behavior, evaluate flags ingetServerSidePropsand pass the result throughpageProps:pages/dashboard.tsxEvent flushing: On Vercel,
@posthog/nextauto-detectswaitUntilfrom@vercel/functionsso events are flushed after the response is sent without blocking it. No manualshutdown()call is needed. In other environments, pass a customwaitUntilin the server client options withcreatePostHog({ options: { waitUntil } }), or events will be batched and flushed normally.Then wire the bootstrap data into the provider:
pages/_app.tsxNote: Avoid capturing page views or other navigation events while rendering a Server Component or running
getServerSideProps. Rendering doesn't prove that a navigation completed. The App Router can execute render code while prefetching, and Next.js can retry renders. UsePostHogPageViewfor page views, and capture server-side events from handlers invoked for the action you want to measure.Dynamic rendering keeps personalized flag decisions out of Next.js's shared Full Route Cache. The App Router can still reuse a React Server Component payload from its client Router Cache. After an authentication or identity change, use
router.refresh()when the current route's server-rendered flag decisions must be evaluated again. - 8
Capture server-side request errors
Recommended@posthog/nextenables automatic browser exception capture by default. On Next.js 15 and later, capture server-side request errors by exportingonRequestErrorfrom your rootinstrumentation.tsfile:instrumentation.tsNext.js calls this hook when a Server Component, Route Handler, or Server Action throws during a request. When available, the PostHog identity cookie links the exception to the same user and session. Import this hook from
@posthog/nextfor both App Router and Pages Router applications.Use
createOnRequestError()when you need to add properties, filter errors, or customize the server client. You can disable automatic browser exception capture withclientOptions={{ capture_exceptions: false }}. - 9
Configure advanced options
OptionalBootstrap feature flags
In the App Router, set
bootstrapFlagsto evaluate feature flags on the server and pass the results to the client, which avoids flag flicker on first render:TSXYou can also limit the flags evaluated and provide properties used for evaluation:
TSXEnabling
bootstrapFlagscallscookies()internally, which opts every page route rendered through that provider into dynamic rendering. If the provider is in your root layout, this includes every page in your app. To preserve static rendering elsewhere, enablebootstrapFlagsin the narrowest layout that needs server-evaluated flags. In the Pages Router, evaluate feature flags insidegetServerSidePropsand pass the result throughclientOptions.bootstrapas shown in the server setup above.Tracing headers
@posthog/nextaddsX-POSTHOG-DISTINCT-IDandX-POSTHOG-SESSION-IDheaders to same-origin browser requests by default. This connects server events and feature flag evaluations to the browser user and session.Pass
clientOptions.tracing_headersto customize the hostnames that receive these headers, or use an empty array to disable them:TSXConsent-aware setup
If your app requires consent before setting cookies, disable anonymous cookie seeding in middleware:
middleware.tsThen initialize the client with capture disabled until you opt in:
TSXAPI proxy options
Pass an object to
proxyto customize the ingest path or host:middleware.tsWhen you change the proxy path, use the same path in
clientOptions.api_host:TSX - 10