Next.js Error Tracking installation

Install with the wizard

Run this command in your project directory to add PostHog to your Next.js project. The wizard detects your framework, installs the SDK, and sets up your first events.

Learn more

Or set it up manually by following the rest of this guide.

  1. Install the package

    Required

    Install the PostHog JavaScript library using your package manager:

    npm install posthog-js
  2. Add environment variables

    Required

    Add your PostHog project token and host to your .env.local file and to your hosting provider (e.g. Vercel, Netlify). These values need to start with NEXT_PUBLIC_ to be accessible on the client-side.

    .env.local
    NEXT_PUBLIC_POSTHOG_PROJECT_TOKEN=<ph_project_token>
    NEXT_PUBLIC_POSTHOG_HOST=https://us.i.posthog.com
  3. Initialize PostHog

    Required

    Choose the integration method based on your Next.js version and router type.

    If you're using Next.js 15.3+, you can use instrumentation-client.ts for a lightweight, fast integration:

    instrumentation-client.ts
    import posthog from 'posthog-js'
    posthog.init(process.env.NEXT_PUBLIC_POSTHOG_PROJECT_TOKEN!, {
    api_host: process.env.NEXT_PUBLIC_POSTHOG_HOST,
    defaults: '2026-08-30'
    })
    Defaults option

    The defaults option automatically configures PostHog with recommended settings for new projects. See SDK defaults for details.

  4. Accessing PostHog on the client

    Recommended

    Once initialized in instrumentation-client.ts, import posthog from posthog-js anywhere and call the methods you need:

    app/checkout/page.tsx
    'use client'
    import posthog from 'posthog-js'
    export default function CheckoutPage() {
    function handlePurchase() {
    posthog.capture('purchase_completed', { amount: 99 })
    }
    return <button onClick={handlePurchase}>Complete purchase</button>
    }
  5. Capture client-side exceptions

    Required

    PostHog can automatically capture unhandled exceptions in your Next.js app using the JavaScript Web SDK. Turn it on with capture_exceptions: true in your posthog.init call:

    instrumentation-client.js
    posthog.init(process.env.NEXT_PUBLIC_POSTHOG_PROJECT_TOKEN, {
    api_host: process.env.NEXT_PUBLIC_POSTHOG_HOST,
    capture_exceptions: true,
    })

    It is also possible to manually capture exceptions using the captureException method:

    JavaScript
    posthog.captureException(error, additionalProperties)
    Manual capture is very useful if you already use error boundaries to handle errors in your app:

    Next.js uses error boundaries to handle uncaught exceptions by rendering a fallback UI instead of the crashing components. To set one up, create a error.tsx file in any of your route directories. This triggers when there is an error rendering your component. An error boundary catches the error before autocapture sees it, so add the capture to every error.tsx your app has. It should look like this:

    error.tsx
    "use client"
    import posthog from "posthog-js"
    import { useEffect } from "react"
    export default function Error({
    error,
    reset,
    }: {
    error: Error & { digest?: string }
    reset: () => void
    }) {
    useEffect(() => {
    posthog.captureException(error)
    }, [error])
    return (
    ...
    )
    }

    Also add the capture to a Global Error component at app/global-error.tsx, and create one if your app has none: it catches errors in the root layout and in the first render, before autocapture loads.

    app/global-error.tsx
    'use client'
    import posthog from "posthog-js"
    import NextError from "next/error"
    import { useEffect } from "react"
    export default function GlobalError({
    error,
    reset,
    }: {
    error: Error & { digest?: string }
    reset: () => void
    }) {
    useEffect(() => {
    posthog.captureException(error)
    }, [error])
    return (
    // global-error must include html and body tags
    <html>
    <body>
    {/* `NextError` is the default Next.js error page component */}
    <NextError statusCode={0} />
    </body>
    </html>
    )
    }
  6. Installing PostHog SDK for server-side

    Required

    Next.js enables you to both server-side render pages and add server-side functionality. To capture server-side exceptions, use @posthog/next for the errors Next.js reports and the Node SDK for everything else.

    First, install @posthog/next 0.11.0+ and posthog-node 5.61.1+, and upgrade posthog-js to 1.445.1+ if yours is older:

    npm install @posthog/next posthog-node

    Then create a lib/posthog-server.js file that initializes PostHog from posthog-node. With enableExceptionAutocapture, it captures uncaught exceptions and unhandled promise rejections. Turn it on for the Node.js runtime only: the edge runtime cannot register process listeners, so the client would fail to start there.

    This looks like this:

    lib/posthog-server.js
    import { PostHog } from 'posthog-node'
    let posthogServer = null
    export function getPostHogServer() {
    posthogServer ??= new PostHog(process.env.NEXT_PUBLIC_POSTHOG_PROJECT_TOKEN, {
    host: process.env.NEXT_PUBLIC_POSTHOG_HOST,
    flushAt: 1,
    flushInterval: 0,
    // Uncaught exceptions and unhandled promise rejections, on the Node.js runtime only
    enableExceptionAutocapture: process.env.NEXT_RUNTIME === 'nodejs',
    })
    return posthogServer
    }

    You can now use the getPostHogServer function to capture exceptions your server-side code handles itself. Pass the user's distinct ID when you know it:

    JavaScript
    const posthog = getPostHogServer()
    try {
    throw new Error('This is a test exception for error tracking')
    } catch (error) {
    posthog.captureException(error, 'user_distinct_id', { source: 'test' })
    }
  7. Verify server-side exceptions

    Recommended

    You should also see events and exceptions in PostHog coming from your server-side code in the activity feed.

    Check for server events in PostHog

  8. Capturing server-side exceptions

    Required

    To capture errors that occur in your server-side code, set up an instrumentation.ts file at the root of your project (or in src/). Next.js calls its onRequestError hook when a Server Component, Route Handler, or Server Action throws. createOnRequestError returns that hook, and links each error to the user and session. register() starts the posthog-node client when the server starts.

    This looks like this:

    instrumentation.js
    import { createOnRequestError } from '@posthog/next'
    import { getPostHogServer } from './lib/posthog-server'
    export function register() {
    if (process.env.NEXT_RUNTIME === 'nodejs') {
    getPostHogServer()
    }
    }
    export const onRequestError = createOnRequestError({
    apiKey: process.env.NEXT_PUBLIC_POSTHOG_PROJECT_TOKEN,
    host: process.env.NEXT_PUBLIC_POSTHOG_HOST,
    })

    onRequestError misses errors thrown in proxy.ts and in Node.js middleware. If your app has either, wrap it with withPostHogProxy:

    proxy.ts
    import { NextResponse } from 'next/server'
    import type { NextRequest } from 'next/server'
    import { withPostHogProxy } from '@posthog/next'
    // On Next.js 15 and earlier, this is middleware.ts with `export const middleware`
    export const proxy = withPostHogProxy(
    async (request: NextRequest) => {
    // Your existing proxy code
    return NextResponse.next()
    },
    {
    apiKey: process.env.NEXT_PUBLIC_POSTHOG_PROJECT_TOKEN,
    host: process.env.NEXT_PUBLIC_POSTHOG_HOST,
    }
    )

    Errors thrown in after() callbacks do not reach onRequestError either. If your app imports after from next/server, capture inside each callback:

    app/api/orders/route.ts
    import { after } from 'next/server'
    import { getPostHogServer } from '@/lib/posthog-server'
    after(async () => {
    try {
    await sendReceipt(order)
    } catch (error) {
    await getPostHogServer().captureExceptionImmediate(error)
    throw error
    }
    })

    Next.js 14 and earlier run instrumentation.js only with the instrumentationHook flag. They never call onRequestError, so leave it out, and upgrade to capture Server Component, Route Handler and Server Action errors:

    next.config.js
    module.exports = {
    // Only on Next.js 14 and earlier: later versions run instrumentation.js without it
    experimental: { instrumentationHook: true },
    }

    You can find a full example of both this and client-side error tracking in our Next.js error monitoring tutorial.

  9. Verify error tracking

    Recommended
    Confirm events are being sent to PostHog
    Before proceeding, let's make sure exception events are being captured and sent to PostHog. You should see events appear in the activity feed.
    Activity feed with events
    Check for exceptions in PostHog
  10. Upload source maps

    Required

    Great, you're capturing exceptions! If you serve minified bundles, the next step is to upload source maps to generate accurate stack traces.

    Let's continue to the next section.

    Upload source maps

Still have questions?

Was this page useful?