--- status: "published" title: 'Creating a Custom Analytics Provider in MakerKit' label: 'Custom Analytics Provider' description: 'Build a custom analytics provider to integrate Mixpanel, Amplitude, Segment, or any analytics service with MakerKit unified analytics API.' order: 5 --- MakerKit's analytics system is provider-agnostic. If your preferred analytics service is not included (Google Analytics, PostHog, Umami), you can create a custom provider that integrates with the unified analytics API. Events dispatched through `analytics.trackEvent()` or App Events will automatically route to your custom provider alongside any other registered providers. ## The AnalyticsService Interface Every analytics provider must implement the `AnalyticsService` interface: ```typescript interface AnalyticsService { initialize(): Promise; identify(userId: string, traits?: Record): Promise; trackPageView(path: string): Promise; trackEvent( eventName: string, eventProperties?: Record ): Promise; } ``` | Method | Purpose | |--------|---------| | `initialize()` | Load scripts, set up the SDK | | `identify()` | Associate a user ID with subsequent events | | `trackPageView()` | Record a page view | | `trackEvent()` | Record a custom event with properties | All methods return Promises. Use `void` when calling from non-async contexts. ## Example: Mixpanel Provider Here is a complete implementation for Mixpanel: ```typescript {% title="packages/analytics/src/mixpanel-service.ts" %} import { NullAnalyticsService } from './null-analytics-service'; import type { AnalyticsService } from './types'; class MixpanelService implements AnalyticsService { private mixpanel: typeof import('mixpanel-browser') | null = null; private token: string; constructor(token: string) { this.token = token; } async initialize(): Promise { if (typeof window === 'undefined') { return; } const mixpanel = await import('mixpanel-browser'); mixpanel.init(this.token, { track_pageview: false, // We handle this manually persistence: 'localStorage', }); this.mixpanel = mixpanel; } async identify(userId: string, traits?: Record): Promise { if (!this.mixpanel) return; this.mixpanel.identify(userId); if (traits) { this.mixpanel.people.set(traits); } } async trackPageView(path: string): Promise { if (!this.mixpanel) return; this.mixpanel.track('Page Viewed', { path }); } async trackEvent( eventName: string, eventProperties?: Record ): Promise { if (!this.mixpanel) return; this.mixpanel.track(eventName, eventProperties); } } export function createMixpanelService(): AnalyticsService { const token = process.env.NEXT_PUBLIC_MIXPANEL_TOKEN; if (!token) { console.warn('Mixpanel token not configured'); return new NullAnalyticsService(); } return new MixpanelService(token); } ``` Install the Mixpanel SDK: ```bash pnpm add mixpanel-browser --filter "@kit/analytics" ``` ## Registering Your Provider Add your custom provider to the analytics manager: ```typescript {% title="packages/analytics/src/index.ts" %} import { createAnalyticsManager } from './analytics-manager'; import { createMixpanelService } from './mixpanel-service'; import type { AnalyticsManager } from './types'; export const analytics: AnalyticsManager = createAnalyticsManager({ providers: { mixpanel: createMixpanelService, }, }); ``` Add environment variables: ```bash {% title=".env.local" %} NEXT_PUBLIC_MIXPANEL_TOKEN=your_mixpanel_token ``` ## Using Multiple Providers Register multiple providers to dispatch events to all of them: ```typescript {% title="packages/analytics/src/index.ts" %} import { createAnalyticsManager } from './analytics-manager'; import { createMixpanelService } from './mixpanel-service'; import { createPostHogAnalyticsService } from '@kit/posthog/client'; export const analytics = createAnalyticsManager({ providers: { mixpanel: createMixpanelService, posthog: createPostHogAnalyticsService, }, }); ``` When you call `analytics.trackEvent()`, both Mixpanel and PostHog receive the event. ## Example: Amplitude Provider Here is a skeleton for Amplitude: ```typescript {% title="packages/analytics/src/amplitude-service.ts" %} import type { AnalyticsService } from './types'; class AmplitudeService implements AnalyticsService { private amplitude: typeof import('@amplitude/analytics-browser') | null = null; async initialize(): Promise { if (typeof window === 'undefined') return; const amplitude = await import('@amplitude/analytics-browser'); const apiKey = process.env.NEXT_PUBLIC_AMPLITUDE_API_KEY; if (apiKey) { amplitude.init(apiKey); this.amplitude = amplitude; } } async identify(userId: string, traits?: Record): Promise { if (!this.amplitude) return; this.amplitude.setUserId(userId); if (traits) { const identifyEvent = new this.amplitude.Identify(); Object.entries(traits).forEach(([key, value]) => { identifyEvent.set(key, value); }); this.amplitude.identify(identifyEvent); } } async trackPageView(path: string): Promise { if (!this.amplitude) return; this.amplitude.track('Page Viewed', { path }); } async trackEvent( eventName: string, eventProperties?: Record ): Promise { if (!this.amplitude) return; this.amplitude.track(eventName, eventProperties); } } export function createAmplitudeService(): AnalyticsService { return new AmplitudeService(); } ``` ## Example: Segment Provider Segment acts as a data router to multiple destinations: ```typescript {% title="packages/analytics/src/segment-service.ts" %} import type { AnalyticsService } from './types'; declare global { interface Window { analytics: { identify: (userId: string, traits?: object) => void; page: (name?: string, properties?: object) => void; track: (event: string, properties?: object) => void; }; } } class SegmentService implements AnalyticsService { async initialize(): Promise { // Segment snippet is typically added via