Skip to content

Marrow-Stack/ms-block-analytics

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

4 Commits
 
 
 
 

Repository files navigation

Analytics Tracker

Typed event tracking for Next.js 14 — PostHog wrapper with auto page view hook, user identification, a self-hosted Supabase fallback, and six pre-written SQL queries for DAU, WAU, funnel, revenue, and retention cohorts.

What's included

PostHog wrapper

  • initAnalytics(config) — initialises PostHog with lazy import; disables autocapture and built-in pageview tracking; accepts posthogKey, apiHost, debug, disabled
  • track(event, properties?) — captures a typed AnalyticsEvent with an auto-added timestamp; logs to console in development; no-ops silently if PostHog isn't loaded
  • identifyUser(userId, traits?) — calls posthog.identify; pass after login/session restore
  • resetAnalytics() — calls posthog.reset; call on logout to disassociate the device
  • trackPageView(url?) — manual page view capture; defaults to window.location.pathname
  • setUserProperty(key, value) — sets a PostHog person property via people.set

React hooks

  • usePageTracking() — auto-fires trackPageView on usePathname changes; deduplicates consecutive fires; drop into a layout component
  • useTrackOnce(event, props?) — fires a single event on mount; useful for impression tracking on detail pages

Self-hosted Supabase fallback

  • trackToSupabase(event, userId, properties?) — POSTs to /api/analytics; includes session ID (from sessionStorage), URL, and referrer; use when PostHog is unavailable or for private data
  • ANALYTICS_API_ROUTE — paste-ready content for app/api/analytics/route.ts; inserts into analytics_events via service role

SQL queries

  • ANALYTICS_QUERIES.dailyActiveUsers — DAU for the last 30 days
  • ANALYTICS_QUERIES.weeklyActiveUsers — WAU for the last 90 days
  • ANALYTICS_QUERIES.topEvents — top 15 events by count in the last 7 days
  • ANALYTICS_QUERIES.conversionFunnel — page view → detail view → purchase started → purchase completed counts for the last 30 days
  • ANALYTICS_QUERIES.revenueByEvent — revenue and purchase count per block_id from purchase_completed events
  • ANALYTICS_QUERIES.retentionCohort — weekly retention cohort table by signup week

Types

  • AnalyticsEvent — union of 21 typed event names
  • EventPropertiesRecord<string, string | number | boolean | null | undefined>

Setup

1. Install dependencies

npm install posthog-js

2. Environment variables

NEXT_PUBLIC_POSTHOG_KEY=phc_xxxxxxxxxxxxxxxxxxxx    # PostHog project API key
NEXT_PUBLIC_POSTHOG_HOST=https://app.posthog.com    # or your self-hosted host

# Self-hosted fallback only:
NEXT_PUBLIC_SUPABASE_URL=your Supabase project URL
SUPABASE_SERVICE_ROLE_KEY=service role key (server-only)

3. Initialise in your providers

// app/providers.tsx
'use client'
import { useEffect } from 'react'
import { initAnalytics, usePageTracking } from '@/blocks/analytics'

function PageTracker() { usePageTracking(); return null }

export function Providers({ children }: { children: React.ReactNode }) {
  useEffect(() => {
    initAnalytics({ posthogKey: process.env.NEXT_PUBLIC_POSTHOG_KEY! })
  }, [])
  return <>{children}<PageTracker /></>
}

4. Self-hosted fallback (optional)

Copy ANALYTICS_API_ROUTE to app/api/analytics/route.ts, then run this in Supabase:

CREATE TABLE analytics_events (
  id         UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  user_id    UUID REFERENCES profiles(id) ON DELETE SET NULL,
  session_id TEXT NOT NULL,
  event      TEXT NOT NULL,
  properties JSONB NOT NULL DEFAULT '{}',
  url        TEXT,
  referrer   TEXT,
  created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX analytics_event_type_idx ON analytics_events(event, created_at DESC);
CREATE INDEX analytics_user_idx       ON analytics_events(user_id, created_at DESC);

Usage examples

// Track events anywhere — server or client
import { track } from '@/blocks/analytics'

// After a successful purchase
track('purchase_completed', { block_id: 'auth', amount: 19, currency: 'USD' })

// After a failed purchase
track('purchase_failed', { block_id: 'auth', reason: 'card_declined' })
// Identify user after login (call once per session)
import { identifyUser, resetAnalytics } from '@/blocks/analytics'

// On sign-in:
identifyUser(session.user.id, {
  email:     session.user.email,
  role:      session.user.role,
  plan:      'pro',
  createdAt: session.user.createdAt,
})

// On sign-out:
resetAnalytics()
// Track a block detail view once on mount
'use client'
import { useTrackOnce } from '@/blocks/analytics'

export function BlockDetailPage({ block }) {
  useTrackOnce('block_detail_viewed', { block_id: block.id, price: block.price })
  return <div>{/* content */}</div>
}

Notes

  • initAnalytics uses a dynamic import('posthog-js') — PostHog is not loaded until initAnalytics is called, so events fired before the async import resolves are silently dropped; call initAnalytics as early as possible (top of providers.tsx)
  • track is a no-op on the server — the _posthog module variable is always null in RSC/API routes; use trackToSupabase for server-side event capture
  • ANALYTICS_QUERIES are plain SQL strings — run them via db.rpc, a Supabase SQL editor, or a migration; they reference the analytics_events table and the properties JSONB column directly, so they only work with the self-hosted fallback, not with PostHog
  • conversionFunnel counts events across all users for the period, not per-user funnel steps — a user who viewed a detail page 10 times counts as 10; for true per-user funnel analysis, use PostHog's built-in funnel charts

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors