Generated banner for Instrument Product Analytics
Generated logo for Instrument Product Analytics

Instrument Product Analytics

PostHogOfficialCreated by PostHog

  • Skill
  • Tool integration
  • Data and AI
  • MIT
  • Low risk

From the author

Add PostHog product analytics events to track user behavior. Use after implementing new features or reviewing PRs to ensure meaningful user actions are captured. Also handles initial PostHog SDK setup if not yet installed.
— the description by PostHog

AutoGineer summary

Written by AutoGineer from the skill’s own files, not by the author

This skill provides a structured, multi-step workflow to integrate PostHog product analytics into a wide variety of software projects. It guides the user through detecting the project's framework, installing the appropriate SDK, initializing the service, and strategically placing event capture calls. The skill emphasizes non-intrusive implementation, requiring that new analytics code be added alongside existing logic without altering the project's fundamental architecture. It also covers user identification, error tracking, and secure environment variable management.

What it does

  • Detects the project's framework and language to determine the correct PostHog SDK.
  • Handles initial SDK installation and initialization based on framework-specific patterns.
  • Identifies key user actions and business-critical events for tracking.
  • Implements event capture calls and user identification logic.
  • Configures environment variables securely for API tokens and host URLs.
  • Verifies the integration and performs targeted linting on modified files.

When to use it

  • After implementing new features that require tracking for product analytics.
  • When reviewing pull requests to ensure meaningful user behaviors are captured.
  • During the initial setup of a new project that needs product analytics.
  • When migrating or adding analytics to an existing application.

Advantages

  • Supports a broad range of frontend, backend, and mobile frameworks.
  • Ensures consistent event naming and property usage across the codebase.
  • Prioritizes non-breaking changes and minimal impact on existing code structure.
  • Automates the complex setup of environment variables and SDK initialization.

Usage examples

“Add PostHog analytics to my Next.js project.”
The agent will analyze the project structure, install the PostHog SDK, initialize it in the appropriate client/server files, and prompt you to identify key events to track.
“I need to track user signups and errors in my Django backend.”
The agent will locate your authentication logic, add identify calls, implement exception capture, and ensure the PostHog project tokens are managed via environment variables.

Install

  1. Download the skill as a ZIP:

    https://mp-staging.autogineer.com/agent-skills/posthog/instrument-product-analytics/download
  2. Unzip it into the cross-client skills folder of your project:

    mkdir -p .agents/skills && unzip instrument-product-analytics.zip -d .agents/skills

Documentation by PostHog

Add PostHog product analytics events

Use this skill to add product analytics events (capture calls) that track meaningful user actions in new or changed code. Use it after implementing features or reviewing PRs to ensure key user behaviors are captured. If PostHog is not yet installed, this skill also covers initial SDK setup. Supports any framework or language.

Supported frameworks and languages: Next.js, React Router, Nuxt, Vue, TanStack Start, SvelteKit, Astro, Angular, Django, Flask, FastAPI, Laravel, PHP, Ruby on Rails, Go, Elixir, Android, iOS, Flutter, React Native, Expo, and more.

Instructions

Follow these steps IN ORDER:

STEP 1: Analyze the codebase and detect the platform.

Look for dependency files (package.json, pubspec.yaml, Podfile, Package.swift, requirements.txt, Gemfile, composer.json, go.mod, mix.exs, etc.) to determine the framework and language.

Look for lockfiles (pnpm-lock.yaml, package-lock.json, yarn.lock, bun.lockb, go.sum, pubspec.lock, Podfile.lock, Package.resolved, mix.lock) to determine the package manager.

  • Check for existing PostHog setup. If PostHog is already installed and initialized, skip to STEP 5.

STEP 2: Research integration. (Skip if PostHog is already set up.) 2.1. Find the reference file below that matches the detected framework — it is the source of truth for SDK initialization, provider setup, and event capture patterns. Read it now. 2.2. If no reference matches, fall back to your general knowledge and web search. Use posthog.com/docs as the primary search source.

STEP 3: Install the PostHog SDK. (Skip if PostHog is already set up.)

  • Add the PostHog SDK package for the detected platform. Do not manually edit package.json — use the package manager's install command.

STEP 4: Initialize PostHog. (Skip if PostHog is already set up.)

  • Follow the framework reference for where and how to initialize. This varies significantly by framework (e.g., instrumentation-client.ts for Next.js 15.3+, AppConfig.ready() for Django, create_app() for Flask).

STEP 5: Plan event tracking.

  • From the project's file list, select between 10 and 15 files that might have interesting business value for event tracking, especially conversion and churn events.

  • Also look for files related to login that could be used for identifying users, along with error handling.

  • Find any existing posthog.capture() code. Make note of event name formatting. Don't duplicate existing events; supplement them.

  • Track actions only, not pageviews (those can be captured automatically). Exceptions can be made for "viewed"-type events at the top of a conversion funnel.

  • Server-side events are REQUIRED if the project includes any instrumentable server-side code (API routes, server actions, webhook handlers, payment/checkout completion, authentication endpoints).

STEP 6: Implement event capture.

  • For each planned event, add posthog.capture() calls with useful properties.

  • If a file already has existing integration code for other tools or services, don't overwrite or remove that code. Place PostHog code below it.

  • Do not alter the fundamental architecture of existing files. Make additions minimal and targeted.

  • You must read a file immediately before attempting to write it.

STEP 7: Identify users.

  • Add PostHog identify() calls on the client side during login and signup events. Use the contents of login and signup forms to identify users on submit.

  • If both frontend and backend exist, pass the client-side session and distinct ID using X-POSTHOG-DISTINCT-ID and X-POSTHOG-SESSION-ID headers to the server-side code. On the server side, make sure events have a matching distinct ID.

STEP 8: Add error tracking.

  • Add PostHog exception capture error tracking to relevant files, particularly around critical user flows and API boundaries.

STEP 9: Set up environment variables.

  • Check if the project already has PostHog environment variables configured (e.g. in .env, .env.local, or framework-specific env files). If valid values already exist, skip this step.

  • If the PostHog project token is missing, use the PostHog MCP server's projects-get tool to retrieve the project's api_token. If multiple projects are returned, ask the user which project to use. If the MCP server is not connected or not authenticated, ask the user for their PostHog project token instead.

  • For the PostHog host URL: check the projects-get MCP response for a region field — US maps to https://us.i.posthog.com, EU maps to https://eu.i.posthog.com. If the region is not available from the MCP response or from existing project configuration, ask the user: "Are you on PostHog US Cloud or EU Cloud?" Do not assume US Cloud.

  • Write these values to the appropriate env file using the framework's naming convention.

  • Reference these environment variables in code instead of hardcoding them.

STEP 10: Verify and clean up.

  • Check the project for errors. Look for type checking or build scripts in package.json.

  • Ensure any components created were actually used.

  • Run any linter or prettier-like scripts found in the package.json, but ONLY on the files you have edited or created during this session. Never run formatting or linting across the entire project's codebase.

Reference files

  • references/EXAMPLE-next-app-router.md - next-app-router example project code

  • references/EXAMPLE-next-pages-router.md - next-pages-router example project code

  • references/EXAMPLE-react-react-router-6.md - react-react-router-6 example project code

  • references/EXAMPLE-react-react-router-7-framework.md - react-react-router-7-framework example project code

  • references/EXAMPLE-react-react-router-7-data.md - react-react-router-7-data example project code

  • references/EXAMPLE-react-react-router-7-declarative.md - react-react-router-7-declarative example project code

  • references/EXAMPLE-nuxt-3-6.md - nuxt-3-6 example project code

  • references/EXAMPLE-nuxt-4.md - nuxt-4 example project code

  • references/EXAMPLE-vue-3.md - vue-3 example project code

  • references/EXAMPLE-react-tanstack-router-file-based.md - react-tanstack-router-file-based example project code

  • references/EXAMPLE-react-tanstack-router-code-based.md - react-tanstack-router-code-based example project code

  • references/EXAMPLE-tanstack-start.md - tanstack-start example project code

  • references/EXAMPLE-sveltekit.md - sveltekit example project code

  • references/EXAMPLE-astro-static.md - astro-static example project code

  • references/EXAMPLE-astro-view-transitions.md - astro-view-transitions example project code

  • references/EXAMPLE-astro-ssr.md - astro-ssr example project code

  • references/EXAMPLE-astro-hybrid.md - astro-hybrid example project code

  • references/EXAMPLE-angular.md - angular example project code

  • references/EXAMPLE-django.md - django example project code

  • references/EXAMPLE-flask.md - flask example project code

  • references/EXAMPLE-fastapi.md - fastapi example project code

  • references/EXAMPLE-python.md - python example project code

  • references/EXAMPLE-laravel.md - laravel example project code

  • references/EXAMPLE-php.md - php example project code

  • references/EXAMPLE-ruby-on-rails.md - ruby-on-rails example project code

  • references/EXAMPLE-ruby.md - ruby example project code

  • references/EXAMPLE-android.md - android example project code

  • references/EXAMPLE-swift.md - swift example project code

  • references/EXAMPLE-react-native.md - react-native example project code

  • references/EXAMPLE-expo.md - expo example project code

  • references/next-js.md - Next.js

  • references/react-router-v6.md - React router v6

  • references/react-router-v7-framework-mode.md - React router v7 framework mode (remix v3)

  • references/react-router-v7-data-mode.md - React router v7 data mode

  • references/react-router-v7-declarative-mode.md - React router v7 declarative mode

  • references/nuxt-js-3-6.md - Nuxt.js (v3.0 to v3.6)

  • references/nuxt-js.md - Nuxt.js

  • references/vue-js.md - Vue.js

  • references/tanstack-start.md - Tanstack start

  • references/svelte.md - Svelte

  • references/astro.md - Astro

  • references/angular.md - Angular

  • references/django.md - Django

  • references/flask.md - Flask

  • references/python.md - Python

  • references/posthog-python.md - PostHog python SDK

  • references/dotnet.md - .net

  • references/elixir.md - Elixir

  • references/go.md - Go

  • references/laravel.md - Laravel

  • references/php.md - Php

  • references/ruby-on-rails.md - Ruby on rails

  • references/ruby.md - Ruby

  • references/android.md - Android

  • references/ios.md - Ios

  • references/usage.md - Ios SDK usage

  • references/configuration.md - Ios SDK configuration

  • references/flutter.md - Flutter

  • references/react-native.md - React native

  • references/identify-users.md - Identify users

  • references/COMMANDMENTS.md - Framework-specific rules the integration must follow

Each framework reference contains SDK-specific installation, initialization, and usage patterns. Find the one matching the user's stack.

Key principles

  • Environment variables: Always use environment variables for PostHog keys. Never hardcode them.

  • Minimal changes: Add PostHog code alongside existing integrations. Don't replace or restructure existing code.

  • Match the docs: Follow the framework reference's initialization and capture patterns exactly.

Files

references/react-router-v7-framework-mode.md

AI agents: this is one page from PostHog's docs. Full index of Markdown docs for LLMs: https://posthog.com/llms.txt

React Router V7 framework mode (Remix V3)

Canonical URL: https://posthog.com/docs/libraries/react-router/react-router-v7-framework-mode

Cloud region: This page uses US Cloud hosts. For an EU Cloud project, use https://eu.i.posthog.com instead of https://us.i.posthog.com (ingestion host), and use https://eu.posthog.com instead of https://us.posthog.com or https://app.posthog.com (app host for links).

On this page

  • Install client-side SDKs

  • Add your environment variables

  • Add the PostHogProvider to your app

  • Verify client-side events are captured

  • Access PostHog methods

  • Identify your user

  • Create an error boundary

  • Tracking element visibility

  • Install server-side SDKs

  • Create a server-side middleware

  • Verify server-side events are captured

  • Next steps

This guide walks you through setting up PostHog for React Router V7 in framework mode. If you're using React Router in another mode, find the guide for that mode in the React Router page. If you're using React with another framework, go to the React integration guide.

  1. 1

    Install client-side SDKs

    Required

    First, you'll need to install posthog-js and @posthog/react using your package manager. These packages allow you to capture client-side events.

    npm
    npm install --save posthog-js @posthog/react
    Yarn
    yarn add posthog-js @posthog/react
    pnpm
    pnpm add posthog-js @posthog/react
    Bun
    bun add posthog-js @posthog/react

    If your site sets a Content-Security-Policy, it needs to allow PostHog. This applies to the snippet and to package installs alike: the SDK lazy-loads extra bundles (session replay, surveys) from PostHog's CDN, and sends events to the ingestion host. PostHog serves from subdomains of posthog.com that change over time, so allow the wildcard:

    script-src 'self' https://*.posthog.com;
    connect-src 'self' https://*.posthog.com;
    worker-src 'self' blob: data:;

    script-src covers the snippet and the lazy-loaded bundles, connect-src covers event ingestion and feature flags, and worker-src covers session replay. The toolbar needs a few more, or use a reverse proxy so everything is first-party. Failing to do so causes silent failures where capture and identify calls never send, so the integration looks complete while zero events arrive. Remember connect-src falls back to default-src, so default-src 'self' blocks event delivery even when the script itself is bundled.

    In framework mode, you'll also need to set posthog-js and @posthog/react as external packages in your vite.config.ts file to avoid SSR errors.

    vite.config.ts

    // ... imports
    
    export default defineConfig({
      plugins: [tailwindcss(), reactRouter(), tsconfigPaths()],
      ssr: {
        noExternal: ['posthog-js', '@posthog/react']
      }
    });
  2. 2

    Add your environment variables

    Required

    Add your environment variables to your .env.local file and to your hosting provider (e.g. Vercel, Netlify, AWS). You can find your project token and host in your project settings. If you're using Vite, prefixing variable names with VITE_ ensures they are accessible in the frontend.

    .env.local

    VITE_POSTHOG_PROJECT_TOKEN=<ph_project_token>
    VITE_POSTHOG_HOST=https://us.i.posthog.com
  3. 3

    Add the PostHogProvider to your app

    Required

    In framework mode, your app enters from the app/entry.client.tsx file. In this file, you'll need to initialize the PostHog SDK and pass it to your app through the PostHogProvider context.

    app/entry.client.tsx

    import { startTransition, StrictMode } from "react";
    import { hydrateRoot } from "react-dom/client";
    import { HydratedRouter } from "react-router/dom";
    
    import posthog from 'posthog-js';
    import { PostHogProvider } from '@posthog/react'
    
    posthog.init(import.meta.env.VITE_POSTHOG_PROJECT_TOKEN, {
      api_host: import.meta.env.VITE_POSTHOG_HOST,
      defaults: '2026-08-30',
      tracing_headers: [ window.location.hostname, 'localhost' ],
    });
    
    startTransition(() => {
      hydrateRoot(
        document,
        {/* Pass PostHog client through PostHogProvider */}
        <PostHogProvider client={posthog}>
          <StrictMode>
            <HydratedRouter />
          </StrictMode>
        </PostHogProvider>,
      );
    });

    To help PostHog track your user sessions across the client and server, you'll need to add the tracing_headers: ['your-backend-hostname1.com', 'your-backend-hostname2.com', ...] option to your PostHog initialization. This adds the X-POSTHOG-DISTINCT-ID and X-POSTHOG-SESSION-ID headers to requests sent to the configured hostnames, which we'll later use on the server-side.

    TypeError: Cannot read properties of undefined

    If you see the error TypeError: Cannot read properties of undefined (reading '...') this is likely because you tried to call a posthog function when posthog was not initialized (such as during the initial render). On purpose, we still render the children even if PostHog is not initialized so that your app still loads even if PostHog can't load.

    To fix this error, add a check that posthog has been initialized such as:

    useEffect(() => {
      posthog?.capture('test') // using optional chaining (recommended)
    
      if (posthog) {
        posthog.capture('test') // using an if statement
      }
    }, [posthog])

    Typescript helps protect against these errors.

  4. Verify client-side events are captured

    Checkpoint

    Confirm that you can capture client-side events and see them in your PostHog project

    At this point, you should be able to capture client-side events and see them in your PostHog project. This includes basic events like page views and button clicks that are autocaptured.

    You can also try to capture a custom event to verify it's working. You can access PostHog in any component using the usePostHog hook.

    import { usePostHog } from '@posthog/react'
    
    function App() {
      const posthog = usePostHog()
      return <button onClick={() => posthog?.capture('button_clicked')}>Click me</button>
    }

    You should see these events in a minute or two in the activity tab.

  5. 4

    Access PostHog methods

    Required

    On the client-side, you can access the PostHog client using the usePostHog hook. This hook returns the initialized PostHog client, which you can use to call PostHog methods. For example:

    import { usePostHog } from '@posthog/react'
    
    function App() {
      const posthog = usePostHog()
      return <button onClick={() => posthog?.capture('button_clicked')}>Click me</button>
    }

    For a complete list of available methods, see the posthog-js documentation.

  6. 5

    Identify your user

    Recommended

    Now that you can capture basic client-side events, you'll want to identify your user so you can associate users with captured events.

    Generally, you identify users when they log in or when they input some identifiable information (e.g. email, name, etc.). You can identify users by calling the identify method on the PostHog client:

    export default function Login() {
      const { user, login } = useAuth();
      const posthog = usePostHog();
    
      const handleLogin = async (e: React.FormEvent) => {
        // existing code to handle login...
        const user = await login({ email, password });
    
        posthog?.identify(user.email,
          {
            email: user.email,
            name: user.name,
          }
        );
        posthog?.capture('user_logged_in');
      };
    
      return (
        <div>
          {/* ... existing code ... */}
          <button onClick={handleLogin}>Login</button>
        </div>
      );
    }

    PostHog automatically generates anonymous IDs for users before they're identified. When you call identify, a new identified person is created. All previous events tracked with the anonymous ID link to the new identified distinct ID, and all future captures on the same browser associate with the identified person.

  7. 6

    Create an error boundary

    Recommended

    PostHog can capture exceptions thrown in your app through an error boundary. React Router in framework mode has a built-in error boundary that you can use to capture exceptions. You can create an error boundary by exporting ErrorBoundary from your app/root.tsx file.

    app/root.tsx

    import { usePostHog } from '@posthog/react'
    
    export function ErrorBoundary({ error }: Route.ErrorBoundaryProps) {
      const posthog = usePostHog();
      posthog?.captureException(error);
    
      // other error handling code...
      return (
        <div>
          <h1>Something went wrong</h1>
          <p>{error.message}</p>
        </div>
      );
    }

    This automatically captures exceptions thrown in your React Router app using the posthog.captureException() method.

  8. 7

    Tracking element visibility

    Recommended

    The PostHogCaptureOnViewed component enables you to automatically capture events when elements scroll into view in the browser. This is useful for tracking impressions of important content, monitoring user engagement with specific sections, or understanding which parts of your page users are actually seeing.

    The component wraps your content and sends a $element_viewed event to PostHog when the wrapped element becomes visible in the viewport. It only fires once per component instance.

    Basic usage:

    import { PostHogCaptureOnViewed } from '@posthog/react'
    
    function App() {
        return (
            <PostHogCaptureOnViewed name="hero-banner">
                <div>Your important content here</div>
            </PostHogCaptureOnViewed>
        )
    }

    With custom properties:

    You can include additional properties with the event to provide more context:

    <PostHogCaptureOnViewed
        name="product-card"
        properties={{
            product_id: '123',
            category: 'electronics',
            price: 299.99
        }}
    >
        <ProductCard />
    </PostHogCaptureOnViewed>

    Tracking multiple children:

    Use trackAllChildren to track each child element separately. This is useful for galleries or lists where you want to know which specific items were viewed:

    <PostHogCaptureOnViewed
        name="product-gallery"
        properties={{ gallery_type: 'featured' }}
        trackAllChildren
    >
        <ProductCard id="1" />
        <ProductCard id="2" />
        <ProductCard id="3" />
    </PostHogCaptureOnViewed>

    When trackAllChildren is enabled, each child element sends its own event with a child_index property indicating its position.

    Custom intersection observer options:

    You can customize when elements are considered "viewed" by passing options to the IntersectionObserver:

    <PostHogCaptureOnViewed
        name="footer"
        observerOptions={{
            threshold: 0.5,  // Element is 50% visible
            rootMargin: '0px'
        }}
    >
        <Footer />
    </PostHogCaptureOnViewed>

    The component passes all other props to the wrapper div, so you can add styling, classes, or other HTML attributes as needed.

  9. 8

    Install server-side SDKs

    Recommended

    Install the PostHog Node SDK using your package manager. This is the SDK you'll use to capture server-side events.

    npm
    npm install posthog-node --save
    Yarn
    yarn add posthog-node
    pnpm
    pnpm add posthog-node
    Bun
    bun add posthog-node
  10. 9

    Create a server-side middleware

    Recommended

    Use one PostHog client for a long-running Node server. Create it in a server-only module and import it from your middleware and tracing setup.

    app/lib/posthog.server.ts

    import { PostHog } from 'posthog-node';
    
    export const posthog = new PostHog(process.env.VITE_POSTHOG_PROJECT_TOKEN!, {
      host: process.env.VITE_POSTHOG_HOST!,
    });

    In React Router 7, enable server middleware in react-router.config.ts:

    react-router.config.ts

    export default {
      future: { v8_middleware: true },
    };

    React Router 8 enables server middleware by default. Add the following middleware. It reads PostHog tracing headers and keeps each request context separate. Missing or blank headers add no ID. Header IDs are analytics context and must not authorize access.

    app/lib/posthog-middleware.server.ts

    import { createContext, type MiddlewareFunction } from 'react-router';
    import { posthog } from './posthog.server';
    
    type RequestIds = { distinctId?: string; sessionId?: string };
    
    export const posthogClientContext = createContext<typeof posthog>();
    export const posthogErrorContext = createContext<RequestIds | undefined>(undefined);
    
    function tracingId(value: string | null): string | undefined {
      return value?.replace(/[\x00-\x1F\x7F-\x9F]/g, '').trim().slice(0, 1000) || undefined;
    }
    
    export const posthogMiddleware: MiddlewareFunction<Response> = async ({ request, context }, next) => {
      const existing = posthog.getContext();
      const data: RequestIds = {
        distinctId: existing?.distinctId ?? tracingId(request.headers.get('x-posthog-distinct-id')),
        sessionId: existing?.sessionId ?? tracingId(request.headers.get('x-posthog-session-id')),
      };
      context.set(posthogClientContext, posthog);
      context.set(posthogErrorContext, data);
    
      return posthog.withContext(data, () => next(), { fresh: true });
    };

    Register the middleware in app/root.tsx. Put an authentication middleware before it if you use posthog.withContext to establish a verified application identity. Existing application IDs take priority over header IDs.

    app/root.tsx

    import { posthogMiddleware } from './lib/posthog-middleware.server';
    
    export const middleware: Route.MiddlewareFunction[] = [
      posthogMiddleware,
      // other middlewares...
    ];

    Work that the request starts keeps its IDs after next() settles. Pass explicit context to separate jobs that need their own IDs. Flush queued events before your platform can stop server work. Call posthog.shutdown() once when the server stops.

    To capture server errors, add the capture call to your existing handleError export in app/entry.server.tsx. The saved IDs remain available when a middleware error reaches this hook outside the scope. Errors before this middleware runs have no saved request identity.

    app/entry.server.tsx

    import type { HandleErrorFunction } from 'react-router';
    import { posthog } from './lib/posthog.server';
    import { posthogErrorContext } from './lib/posthog-middleware.server';
    
    export const handleError: HandleErrorFunction = (error, { context }) => {
      const saved = context.get(posthogErrorContext);
      posthog.captureException(error, saved?.distinctId,
        saved?.sessionId ? { $session_id: saved.sessionId } : undefined);
    };
    Optional server span context

    Use the optional posthog-node/otel entrypoint when your Node SDK includes it. Add new PostHogContextSpanProcessor(posthog) to the processors in your existing OpenTelemetry setup. Import the processor from posthog-node/otel and reuse the client from posthog.server.ts. Keep your provider, exporters, and sampler.

    Import the helper in posthog-middleware.server.ts:

    import { enrichCurrentSpan } from 'posthog-node/otel';

    Inside the withContext callback, call it immediately before next():

    enrichCurrentSpan(posthog);
          return await next();

    The helper fills missing posthogDistinctId and sessionId attributes on the active span and its nearest recording server ancestor whose parent chain the processor observed. Explicit span attributes stay unchanged. The processor adds the request IDs to new child spans. If a sampler drops an intermediate span, the earlier server span stays unchanged because its parent chain cannot be verified.

    React Router provides server instrumentation hooks for application-owned tracing. Their stable export, instrumentations, is available from 7.15.0. This setup does not create request spans. Configure those hooks or your existing server tracing to start a span before the middleware runs. This recipe was tested with React Router 7.15.0 and 8.4.0 on Node.

  11. Verify server-side events are captured

    Checkpoint

    Confirm that you can capture server-side events and see them in your PostHog project

    At this point, you should be able to capture server-side events and see them in your PostHog project.

    In a route, you can access the PostHog client from the context and capture an event. The middleware assigns the session ID and the distinct ID. This ensures that the system associates events with the correct user and session.

    app/routes/api.checkout.ts

    import { posthogClientContext } from "../lib/posthog-middleware.server";
    
    export async function action({ request, context }: Route.ActionArgs) {
      const body = await request.json();
      // ... existing code ...
    
      // Access the PostHog client from the context and capture an event
      const posthog = context.get(posthogClientContext);
      posthog?.capture({ event: 'checkout_completed' });
    
      return Response.json({
        success: true,
        // ... existing code ...
      });
    }
  12. 10

    Next steps

    Recommended

    Now that you've set up PostHog for React Router, you can start capturing events and exceptions in your app.

    To get the most out of PostHog, you should familiarize yourself with the following:

    • PostHog Web SDK docs: Learn more about the PostHog Web SDK and how to use it on the client-side.

    • PostHog Node SDK docs: Learn more about the PostHog Node SDK and how to use it on the server-side.

    • Identify users: Learn more about how to identify users in your app.

    • Group analytics: Learn more about how to use group analytics in your app.

    • PostHog AI: After capturing events, use PostHog AI to help you understand your data and build insights.

    • Feature flags and experiments: Feature flag and experiment setup is the same as React. You can find more details in the React integration guide.

Requirements and compatibility

Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta

“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.

Needs: Needs an MCP server, Needs Python, Needs Node.js

Security

What our scanner noticed· Low risk

  • Instructions that use the network

A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.

Attribution and licence

Created by
PostHog
Publisher
PostHog (Official)
Source
PostHog/skills · skills/omnibus/instrument-product-analytics · commit a5d30e7
Licence
MIT License (redistribution allowed)
  • LICENSE (added by AutoGineer from LICENSE)
  • ATTRIBUTION.md (written by AutoGineer)
Synced

AutoGineer is not affiliated with or endorsed by PostHog.

Download ZIP

The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download instrument-product-analytics.zip