Documentation

Analytics (PostHog)

PostHog product analytics — client and server tracking, events, and the analytics dashboard.

Open inChatGPT (opens in a new tab)Claude (opens in a new tab)Cursor (opens in a new tab)PostHog product analytics — client and server tracking, events, and the analytics dashboard.

Motoko Base uses PostHog for product analytics. PostHog is separate from Sentry (errors) — do not mix them.

Official docs: PostHog documentation

Setup

  1. Create a PostHog project
  2. Copy the Project API key (client capture)
  3. For the Analytics dashboard (/dashboard/analytics), also create a Personal API key with Query Read
  4. Set env vars:
VariableScopePurpose
NEXT_PUBLIC_POSTHOG_PROJECT_TOKENClientEvent capture
NEXT_PUBLIC_POSTHOG_HOSTClientIngest host (US/EU)
POSTHOG_PERSONAL_API_KEYServerHogQL queries
POSTHOG_PROJECT_IDServerProject ID for queries
POSTHOG_HOSTServerAPI host for queries (not ingest)

Without the project token, analytics is disabled — app works normally.

Client vs server tracking

LayerModuleUse for
Clientsrc/lib/analytics/client.tsBrowser events (sign-in, sign-up)
Serversrc/lib/analytics/server.tsServer actions, webhooks (links, files, billing)

Client init: src/instrumentation-client.ts. Server uses a module singleton — fail-open, never throws.

Events

Event names live in src/lib/analytics/events.ts:

export const ANALYTICS_EVENTS = {
  userSignedUp: "user_signed_up",
  linkCreated: "link_created",
  fileUploaded: "file_uploaded",
  checkoutStarted: "checkout_started",
  // ...
} as const;

Convention: {object}_{past_tense_verb} (lowercase, underscores).

Add a custom event

  1. Add the event name to ANALYTICS_EVENTS in events.ts
  2. Add typed properties to AnalyticsEventProperties (optional)
  3. Capture from the right layer:
// Client component
import { captureClientEvent } from "@/lib/analytics/client";
captureClientEvent(ANALYTICS_EVENTS.linkCreated, { link_id, slug, destination_host });

// Server action
import { captureServerEvent } from "@/lib/analytics/server";
captureServerEvent({ distinctId: userId, event: ANALYTICS_EVENTS.linkCreated, properties: { ... } });

Properties are scrubbed before send (src/lib/analytics/scrub.ts) — no passwords, tokens, or raw URLs with secrets.

Analytics dashboard

/dashboard/analytics runs HogQL queries via src/lib/analytics/query.ts. Requires POSTHOG_PERSONAL_API_KEY + POSTHOG_PROJECT_ID. Results are cached ~60s per user.

Where to change this

WhatWhere
Event taxonomysrc/lib/analytics/events.ts
Client capturesrc/lib/analytics/client.ts
Server capturesrc/lib/analytics/server.ts
Config & enable checkssrc/lib/analytics/config.ts
Dashboard queriessrc/lib/analytics/query.ts
Analytics UIsrc/features/analytics/

Environment Variables — all PostHog env vars.