coldstart

Analytics

PostHog analytics for web and mobile — events, feature flags, session replay

Coldstart generates PostHog analytics when your project includes a web or mobile platform. Each platform gets its own SDK integration, auto-initialized at app startup.

What's generated

posthog-js with a React provider:

posthog.ts
posthog-provider.tsx

src/lib/posthog.ts — PostHog client initialization:

apps/web/src/lib/posthog.ts
import posthog from "posthog-js";

export const initPostHog = () => {
  if (typeof window !== "undefined" && process.env.NEXT_PUBLIC_POSTHOG_KEY) {
    posthog.init(process.env.NEXT_PUBLIC_POSTHOG_KEY, {
      api_host: process.env.NEXT_PUBLIC_POSTHOG_HOST || "https://eu.i.posthog.com",
    });
  }
};

export { posthog };

src/components/posthog-provider.tsx — wraps the app in the root layout:

apps/web/src/components/posthog-provider.tsx
"use client";

import { useEffect } from "react";
import { initPostHog } from "@/lib/posthog";

export const PostHogProvider = ({ children }: { children: React.ReactNode }) => {
  useEffect(() => {
    initPostHog();
  }, []);

  return <>{children}</>;
};

The provider is automatically added to the root layout. PostHog only initializes when NEXT_PUBLIC_POSTHOG_KEY is set, so it's safe in development without credentials.

posthog-react-native with helper functions:

posthog.ts
analytics.ts

lib/posthog.ts — async initialization, disabled in dev:

apps/mobile/lib/posthog.ts
import PostHog from "posthog-react-native";

let posthogClient: PostHog | null = null;

export const initPostHog = async () => {
  const apiKey = process.env.EXPO_PUBLIC_POSTHOG_KEY;
  if (!apiKey || __DEV__) return;

  posthogClient = await PostHog.initAsync(apiKey, {
    host: process.env.EXPO_PUBLIC_POSTHOG_HOST || "https://eu.i.posthog.com",
  });
};

export const getPostHog = () => posthogClient;

lib/analytics.ts — typed helper functions:

apps/mobile/lib/analytics.ts
import { getPostHog } from "./posthog";

export const trackEvent = (event: string, properties?: Record<string, unknown>) => {
  getPostHog()?.capture(event, properties);
};

export const identifyUser = (userId: string, traits?: Record<string, unknown>) => {
  getPostHog()?.identify(userId, traits);
};

export const resetUser = () => {
  getPostHog()?.reset();
};

export const trackScreen = (screenName: string, properties?: Record<string, unknown>) => {
  getPostHog()?.screen(screenName, properties);
};

initPostHog() is called in the root _layout.tsx via useEffect. It's a no-op in development (__DEV__).

Environment variables

VariableRequiredDescription
NEXT_PUBLIC_POSTHOG_KEYYesPostHog project API key
NEXT_PUBLIC_POSTHOG_HOSTNoPostHog instance URL (defaults to https://eu.i.posthog.com)

Add to apps/web/.env:

apps/web/.env
NEXT_PUBLIC_POSTHOG_KEY=phc_your_project_key
NEXT_PUBLIC_POSTHOG_HOST=https://eu.i.posthog.com
VariableRequiredDescription
EXPO_PUBLIC_POSTHOG_KEYYesPostHog project API key
EXPO_PUBLIC_POSTHOG_HOSTNoPostHog instance URL (defaults to https://eu.i.posthog.com)

Add to apps/mobile/.env:

apps/mobile/.env
EXPO_PUBLIC_POSTHOG_KEY=phc_your_project_key
EXPO_PUBLIC_POSTHOG_HOST=https://eu.i.posthog.com

PostHog defaults to the EU instance (eu.i.posthog.com). Change POSTHOG_HOST to https://us.i.posthog.com for the US instance or your self-hosted URL.

Tracking events

Import posthog from the lib and call capture:

Tracking a custom event (web)
import { posthog } from "@/lib/posthog";

// Track a custom event
posthog.capture("feature_used", {
  feature: "export",
  format: "pdf",
});

// Identify the user after login
posthog.identify(user.id, {
  email: user.email,
  name: user.name,
});

// Reset on logout
posthog.reset();

Use the typed helper functions from lib/analytics.ts:

Tracking a custom event (mobile)
import { trackEvent, identifyUser, resetUser, trackScreen } from "@/lib/analytics";

// Track a custom event
trackEvent("feature_used", {
  feature: "export",
  format: "pdf",
});

// Identify the user after login
identifyUser(user.id, {
  email: user.email,
  name: user.name,
});

// Track screen views
trackScreen("Settings");

// Reset on logout
resetUser();

Feature flags

PostHog feature flags work on both platforms:

Feature flags (web)
import { posthog } from "@/lib/posthog";

// Check a boolean flag
if (posthog.isFeatureEnabled("new-dashboard")) {
  // Show new dashboard
}

// Get a multivariate flag value
const variant = posthog.getFeatureFlag("pricing-experiment");
if (variant === "variant-a") {
  // Show variant A pricing
}

// React hook pattern
import { useFeatureFlagEnabled } from "posthog-js/react";

const MyComponent = () => {
  const showBeta = useFeatureFlagEnabled("beta-features");
  if (!showBeta) return null;
  return <BetaFeature />;
};
Feature flags (mobile)
import { getPostHog } from "@/lib/posthog";

// Check a boolean flag
const isEnabled = await getPostHog()?.isFeatureEnabled("new-onboarding");

// Get a multivariate flag value
const variant = await getPostHog()?.getFeatureFlag("pricing-experiment");

Session replay (Web only)

PostHog session replay records user sessions for debugging. Enable it in PostHog initialization:

apps/web/src/lib/posthog.ts (with session replay)
export const initPostHog = () => {
  if (typeof window !== "undefined" && process.env.NEXT_PUBLIC_POSTHOG_KEY) {
    posthog.init(process.env.NEXT_PUBLIC_POSTHOG_KEY, {
      api_host: process.env.NEXT_PUBLIC_POSTHOG_HOST || "https://eu.i.posthog.com",
      session_recording: {
        recordCrossOriginIframes: true,
      },
      capture_pageview: true,
      capture_pageleave: true,
    });
  }
};

Session replay must also be enabled in your PostHog project settings. The SDK flag alone is not enough.

When analytics is generated

Analytics is conditionally generated based on your platform selection:

Platform selectedPostHog generated
Web onlyposthog-js + provider
Mobile onlyposthog-react-native + helpers
Web + MobileBoth
API onlyNo analytics generated
DesktopInherits web analytics (shares Next.js frontend)

The analytics generator runs after the platform generators, patching existing layout files to add the PostHog initialization.

On this page