Mixpanel Analytics Integration
Mixpanel is event-based analytics focused on user behavior rather than pageviews. Instead of counting views, you count events: who clicked, when, with what properties. Strengths include retention analysis, funnels, A/B reports, and cohort building without SQL.
SDK Installation
Two options: NPM package for SPA or CDN script for traditional sites.
NPM:
npm install mixpanel-browser
// src/analytics/mixpanel.ts
import mixpanel from 'mixpanel-browser';
mixpanel.init(import.meta.env.VITE_MIXPANEL_TOKEN, {
debug: import.meta.env.DEV,
track_pageview: false, // manage manually
persistence: 'localStorage',
ignore_dnt: false,
batch_requests: true,
batch_flush_interval_ms: 5000,
});
export default mixpanel;
CDN for traditional sites:
<script>
(function(f,b){if(!b.__SV){var e,g,i,h;window.mixpanel=b;b._i=[];b.init=function(e,f,c){function g(a,d){var b=d.split(".");2==b.length&&(a=a[b[0]],d=b[1]);a[d]=function(){a.push([d].concat(Array.prototype.slice.call(arguments,0)))}}var a=b;"undefined"!==typeof c?a=b[c]=[]:c="mixpanel";a.people=a.people||[];a.toString=function(a){var d="mixpanel";"mixpanel"!==c&&(d+="."+c);a||(d+=" (stub)");return d};a.people.toString=function(){return a.toString(1)+".people (stub)"};i="disable time_event track track_pageview track_links track_forms track_with_groups add_group set_group remove_group register register_once alias unregister identify name_tag set_config reset opt_in_tracking opt_out_tracking has_opted_in_tracking has_opted_out_tracking clear_opt_in_out_tracking start_batch_senders people.set people.set_once people.unset people.increment people.append people.union people.track_charge people.clear_charges people.delete_user people.remove".split(" ");for(h=0;h<i.length;h++)g(a,i[h]);var j="set set_once union unset remove delete".split(" ");a.get_group=function(){function b(c){d[c]=function(){call2_args=arguments;call2=[c].concat(Array.prototype.slice.call(call2_args,0));a.push([e].concat([call2]))}}for(var d={},e=["get_group"].concat(Array.prototype.slice.call(arguments,0)),c=0;c<j.length;c++)b(j[c]);return d};b._i.push([e,f,c])};b.__SV=1.2;e=f.createElement("script");e.type="text/javascript";e.async=!0;e.src="undefined"!==typeof MIXPANEL_CUSTOM_LIB_URL?MIXPANEL_CUSTOM_LIB_URL:"file:"===f.location.protocol&&"//cdn.mxpnl.com/libs/mixpanel-2-latest.min.js".match(/^\/\//)?"https://cdn.mxpnl.com/libs/mixpanel-2-latest.min.js":"//cdn.mxpnl.com/libs/mixpanel-2-latest.min.js";g=f.getElementsByTagName("script")[0];g.parentNode.insertBefore(e,g)}})(document,window.mixpanel||[]);
mixpanel.init('YOUR_TOKEN');
</script>
Event Tracking
Mixpanel is built on three primitives: track, identify, people.set.
// Basic events
mixpanel.track('Page Viewed', {
page_title: document.title,
page_url: window.location.pathname,
referrer: document.referrer || 'direct',
});
// Event with details
mixpanel.track('Button Clicked', {
button_text: 'Submit Request',
button_location: 'hero_section',
page: window.location.pathname,
});
// Form submitted
mixpanel.track('Form Submitted', {
form_name: 'contact_form',
has_phone: true,
source_page: window.location.pathname,
});
// E-commerce
mixpanel.track('Purchase Completed', {
order_id: 'ORDER-789',
revenue: 14500,
currency: 'RUB',
items_count: 3,
category: 'software',
});
User Identification
Without identify, each session is an anonymous user. After login or email form submission, link the anonymous profile to the real one:
// After successful login
function onUserLoggedIn(user: { id: string; email: string; name: string; plan: string }) {
mixpanel.identify(user.id);
mixpanel.people.set({
$email: user.email,
$name: user.name,
plan: user.plan,
created_at: new Date().toISOString(),
});
// Additional properties included in all future events
mixpanel.register({
user_id: user.id,
user_plan: user.plan,
});
}
// If user submitted a form without registering
function onLeadFormSubmitted(email: string) {
mixpanel.alias(email); // links anonymous ID with email
mixpanel.people.set({ $email: email });
}
Super Properties and Context
Super properties are automatically added to every subsequent event — useful for A/B variants, app version, language:
// Set once on initialization
mixpanel.register({
app_version: '2.4.1',
locale: navigator.language,
ab_variant: getABVariant(),
device_type: /Mobi/.test(navigator.userAgent) ? 'mobile' : 'desktop',
});
// register_once — won't overwrite if already set
mixpanel.register_once({
first_visit_date: new Date().toISOString(),
landing_page: window.location.pathname,
utm_source: new URLSearchParams(window.location.search).get('utm_source') ?? 'direct',
});
React Router Integration
// hooks/useMixpanelPageview.ts
import { useEffect } from 'react';
import { useLocation } from 'react-router-dom';
import mixpanel from '@/analytics/mixpanel';
export function useMixpanelPageview() {
const location = useLocation();
useEffect(() => {
mixpanel.track('Page Viewed', {
page_path: location.pathname,
page_search: location.search,
page_hash: location.hash,
});
}, [location.pathname]);
}
Server-side Tracking
For events that only happen on the backend (payment via webhook, account activation via email), use Mixpanel HTTP API:
// app/Services/MixpanelService.php
class MixpanelService
{
private string $token;
private string $endpoint = 'https://api.mixpanel.com/track';
public function __construct()
{
$this->token = config('services.mixpanel.token');
}
public function track(string $event, string $distinctId, array $properties = []): bool
{
$data = [
'event' => $event,
'properties' => array_merge($properties, [
'token' => $this->token,
'distinct_id' => $distinctId,
'time' => time(),
'$insert_id' => uniqid('srv_', true),
]),
];
$response = Http::asForm()->post($this->endpoint, [
'data' => base64_encode(json_encode([$data])),
]);
return $response->body() === '1';
}
public function setPeopleProperties(string $distinctId, array $properties): bool
{
$data = [
'$token' => $this->token,
'$distinct_id' => $distinctId,
'$set' => $properties,
];
$response = Http::asForm()->post('https://api.mixpanel.com/engage', [
'data' => base64_encode(json_encode([$data])),
]);
return $response->body() === '1';
}
}
Data Export and Lookup Tables
For data joining (e.g., add product category to purchase events), Mixpanel supports Lookup Tables:
# Upload lookup table via API
curl -X POST 'https://api.mixpanel.com/lookup-tables/v1/TABLE_ID' \
-H 'Authorization: Basic BASE64_ENCODED_SERVICE_ACCOUNT' \
-F '[email protected]'
CSV format:
id,category,brand,price_tier
SKU-001,software,acme,enterprise
SKU-002,hardware,techco,smb
Debugging
// Enable debug mode manually
mixpanel.set_config({ debug: true });
// Console will show logs for each track call:
// [Mixpanel] Sending request: POST https://api.mixpanel.com/track/...
// [Mixpanel] Response: 1
// Check event queue before initialization
console.log(window.mixpanel);
Mixpanel extension in Chrome DevTools shows all events in real-time without additional setup.
Timeline
SDK installation and basic event tracking — 4–6 hours. Full identify/alias setup, super properties, server-side events — 1–2 days. Funnel and dashboard configuration in account — 4–8 hours separately.







