Skip to main content
Technology & EngineeringAnalytics Services306 lines

Mixpanel Analytics

"Mixpanel: event-based analytics, funnels, retention, user profiles, cohorts, group analytics, JavaScript/Node SDK"

Quick Summary21 lines
Mixpanel is an event-based analytics platform focused on understanding user behavior through
actions rather than page views. The core model revolves around tracking discrete events with
rich properties, building user profiles, and analyzing sequences of actions through funnels
and retention charts. Mixpanel encourages a "track actions, not pages" mindset where every

## Key Points

- **Define a tracking plan** before instrumenting. Document every event name, its properties,
- **Use `mixpanel.time_event()`** to measure durations of multi-step flows like onboarding,
- **Register super properties** for context that applies to every event (app version, platform,
- **Call `mixpanel.reset()` on logout** to clear the identity and super properties, preventing
- **Prefer `track_with_groups`** over manually adding group IDs as event properties. This ensures
- **Batch requests in production** by enabling `batch_requests` to reduce network overhead and
- **Use `register_once` for attribution** data like first-touch channel or referral source so the
- **Tracking page views as the primary metric.** Mixpanel is designed for event-based analysis.
- **Creating too many unique event names.** Events like `clicked_button_save_v2_modal` create
- **Calling `mixpanel.alias()` after `identify()`.** The alias call must happen before identify
- **Sending arrays of objects as properties.** Mixpanel flattens complex nested structures
- **Ignoring `distinct_id` on server-side events.** Every server-side track call requires an
skilldb get analytics-services-skills/Mixpanel AnalyticsFull skill: 306 lines
Paste into your CLAUDE.md or agent config

Mixpanel Analytics

Core Philosophy

Mixpanel is an event-based analytics platform focused on understanding user behavior through actions rather than page views. The core model revolves around tracking discrete events with rich properties, building user profiles, and analyzing sequences of actions through funnels and retention charts. Mixpanel encourages a "track actions, not pages" mindset where every meaningful interaction is captured as a named event with context-rich properties, enabling deep analysis of conversion paths, user segments, and product engagement patterns.

Setup

Client-Side Initialization

// lib/mixpanel.ts
import mixpanel, { type Config } from "mixpanel-browser";

const MIXPANEL_TOKEN = process.env.NEXT_PUBLIC_MIXPANEL_TOKEN!;

const config: Partial<Config> = {
  debug: process.env.NODE_ENV === "development",
  track_pageview: "url-with-path",
  persistence: "localStorage",
  ignore_dnt: false,
  batch_requests: true,
  batch_size: 10,
  batch_flush_interval_ms: 5000,
};

export function initMixpanel(): void {
  mixpanel.init(MIXPANEL_TOKEN, config);
}

export { mixpanel };

Node.js Server SDK

// lib/mixpanel-server.ts
import Mixpanel from "mixpanel";

const mixpanelServer = Mixpanel.init(process.env.MIXPANEL_TOKEN!, {
  protocol: "https",
  geolocate: true,
});

export { mixpanelServer };

React Provider

// app/providers.tsx
"use client";

import { useEffect } from "react";
import { initMixpanel } from "@/lib/mixpanel";

export function AnalyticsProvider({ children }: { children: React.ReactNode }) {
  useEffect(() => {
    initMixpanel();
  }, []);

  return <>{children}</>;
}

Key Techniques

Event Tracking with Properties

import { mixpanel } from "@/lib/mixpanel";

// Track a user action with context
function trackSearch(query: string, resultCount: number, filters: string[]): void {
  mixpanel.track("Search Performed", {
    query,
    result_count: resultCount,
    filters_applied: filters,
    has_filters: filters.length > 0,
    search_source: "main_search_bar",
  });
}

// Track a purchase event for funnel analysis
function trackPurchase(order: {
  id: string;
  items: Array<{ sku: string; price: number }>;
  total: number;
  coupon?: string;
}): void {
  mixpanel.track("Order Completed", {
    order_id: order.id,
    item_count: order.items.length,
    order_total: order.total,
    has_coupon: !!order.coupon,
    coupon_code: order.coupon,
    items: order.items.map((i) => i.sku),
  });
}

User Identity and Profiles

import { mixpanel } from "@/lib/mixpanel";

// Identify user after authentication
function identifyUser(user: {
  id: string;
  email: string;
  name: string;
  createdAt: Date;
  plan: string;
}): void {
  mixpanel.identify(user.id);

  mixpanel.people.set({
    $email: user.email,
    $name: user.name,
    $created: user.createdAt.toISOString(),
    plan: user.plan,
    last_login: new Date().toISOString(),
  });

  // Increment a counter on the profile
  mixpanel.people.increment("login_count", 1);
}

// Reset identity on logout
function handleLogout(): void {
  mixpanel.track("User Logged Out");
  mixpanel.reset();
}

// Link anonymous activity to identified user
function handleSignup(userId: string): void {
  // This merges pre-signup anonymous events with the new user profile
  mixpanel.alias(userId);
  mixpanel.identify(userId);
}

Group Analytics for B2B

import { mixpanel } from "@/lib/mixpanel";

// Associate user with a company for group analytics
function setCompanyGroup(company: {
  id: string;
  name: string;
  plan: string;
  employeeCount: number;
}): void {
  mixpanel.set_group("company", company.id);

  mixpanel.get_group("company", company.id).set({
    $name: company.name,
    plan: company.plan,
    employee_count: company.employeeCount,
    industry: "technology",
  });
}

// Track event with group context
function trackFeatureUsage(feature: string, companyId: string): void {
  mixpanel.track_with_groups(
    "Feature Used",
    { feature_name: feature, timestamp: Date.now() },
    { company: companyId }
  );
}

Server-Side Tracking

import { mixpanelServer } from "@/lib/mixpanel-server";

// Track server-side events (webhooks, background jobs)
function trackServerEvent(
  userId: string,
  event: string,
  properties: Record<string, unknown>
): void {
  mixpanelServer.track(event, {
    distinct_id: userId,
    ...properties,
    $source: "server",
  });
}

// Update user profile from the server
function updateUserProfile(userId: string, properties: Record<string, unknown>): void {
  mixpanelServer.people.set(userId, properties);
}

// Track revenue on user profile
function trackRevenue(userId: string, amount: number, plan: string): void {
  mixpanelServer.people.track_charge(userId, amount, {
    plan,
    date: new Date().toISOString(),
  });
}

Super Properties and Registration

import { mixpanel } from "@/lib/mixpanel";

// Set properties that are sent with every subsequent event
function registerGlobalProperties(appVersion: string, environment: string): void {
  mixpanel.register({
    app_version: appVersion,
    environment,
    platform: "web",
  });
}

// Set a property once (will not overwrite if already set)
function registerFirstTouchChannel(channel: string): void {
  mixpanel.register_once({
    first_touch_channel: channel,
    first_visit_date: new Date().toISOString(),
  });
}

// Time an event to measure duration
function startTimingOnboarding(): void {
  mixpanel.time_event("Onboarding Completed");
}

function completeOnboarding(stepsCompleted: number): void {
  // Mixpanel automatically adds $duration since time_event was called
  mixpanel.track("Onboarding Completed", {
    steps_completed: stepsCompleted,
    completed: stepsCompleted === 5,
  });
}

Reusable Tracking Hook

// hooks/useTrack.ts
"use client";

import { useCallback } from "react";
import { mixpanel } from "@/lib/mixpanel";

type TrackFn = (event: string, properties?: Record<string, unknown>) => void;

export function useTrack(): TrackFn {
  return useCallback((event: string, properties?: Record<string, unknown>) => {
    mixpanel.track(event, {
      ...properties,
      page_url: window.location.pathname,
      timestamp: Date.now(),
    });
  }, []);
}

Best Practices

  • Define a tracking plan before instrumenting. Document every event name, its properties, and expected types in a shared spreadsheet or wiki to keep data consistent.
  • Use mixpanel.time_event() to measure durations of multi-step flows like onboarding, checkout, or form completion without manual timestamp arithmetic.
  • Register super properties for context that applies to every event (app version, platform, user plan) so individual track calls stay focused on action-specific data.
  • Call mixpanel.reset() on logout to clear the identity and super properties, preventing events from being attributed to the wrong user on shared devices.
  • Prefer track_with_groups over manually adding group IDs as event properties. This ensures proper association in Mixpanel's group analytics features.
  • Batch requests in production by enabling batch_requests to reduce network overhead and improve page performance.
  • Use register_once for attribution data like first-touch channel or referral source so the original value is preserved even if the user returns through a different channel.

Anti-Patterns

  • Tracking page views as the primary metric. Mixpanel is designed for event-based analysis. Focusing on page views misses the behavioral insights that funnels and retention provide.
  • Creating too many unique event names. Events like clicked_button_save_v2_modal create fragmented data. Use a consistent event name with differentiating properties instead.
  • Calling mixpanel.alias() after identify(). The alias call must happen before identify on first login. Reversing the order breaks identity merging permanently.
  • Sending arrays of objects as properties. Mixpanel flattens complex nested structures unpredictably. Keep properties as primitives, arrays of strings, or arrays of numbers.
  • Ignoring distinct_id on server-side events. Every server-side track call requires an explicit distinct_id. Omitting it creates orphan events that cannot be tied to users.
  • Not using $ignore_time when backfilling. When importing historical data, set $ignore_time: true to prevent Mixpanel from updating the "last seen" timestamp on profiles.

Install this skill directly: skilldb add analytics-services-skills

Get CLI access →