diff --git a/docs/content/scripts/mixpanel.md b/docs/content/scripts/mixpanel.md new file mode 100644 index 00000000..7a359f59 --- /dev/null +++ b/docs/content/scripts/mixpanel.md @@ -0,0 +1,112 @@ +--- + +title: Mixpanel +description: Use Mixpanel in your Nuxt app. +links: +- label: Source + icon: i-simple-icons-github + to: https://github.com/nuxt/scripts/blob/main/src/runtime/registry/mixpanel-analytics.ts + size: xs + +--- + +[Mixpanel](https://mixpanel.com) is a product analytics platform that helps you understand how users interact with your application through event tracking, funnels, and retention analysis. + +Nuxt Scripts provides a registry script composable [`useScriptMixpanelAnalytics()`](/scripts/mixpanel){lang="ts"} to easily integrate Mixpanel in your Nuxt app. + +::script-stats +:: + +::script-docs +:: + +::script-types +:: + +## Composable Usage + +The simplest way to load Mixpanel is through `nuxt.config`: + +```ts +export default defineNuxtConfig({ + scripts: { + registry: { + mixpanelAnalytics: { + token: 'YOUR_PROJECT_TOKEN', + } + } + } +}) +``` + +### Tracking Events + +```vue + +``` + +### Identifying Users + +```vue + +``` + +### Registering Super Properties + +Super properties are sent with every subsequent event: + +```vue + +``` + +### Environment Variables + +```ts [nuxt.config.ts] +export default defineNuxtConfig({ + scripts: { + registry: { + mixpanelAnalytics: true, + } + }, + runtimeConfig: { + public: { + scripts: { + mixpanelAnalytics: { + token: '', // NUXT_PUBLIC_SCRIPTS_MIXPANEL_ANALYTICS_TOKEN + }, + }, + }, + }, +}) +``` + +```text [.env] +NUXT_PUBLIC_SCRIPTS_MIXPANEL_ANALYTICS_TOKEN=YOUR_PROJECT_TOKEN +``` diff --git a/src/module.ts b/src/module.ts index 9a7755ea..ea42b1f7 100644 --- a/src/module.ts +++ b/src/module.ts @@ -166,6 +166,7 @@ const PARTYTOWN_FORWARDS: Record = { umami: ['umami', 'umami.track'], matomo: ['_paq.push'], segment: ['analytics', 'analytics.track', 'analytics.page', 'analytics.identify'], + mixpanelAnalytics: ['mixpanel', 'mixpanel.track', 'mixpanel.identify', 'mixpanel.people.set', 'mixpanel.reset', 'mixpanel.register'], metaPixel: ['fbq'], xPixel: ['twq'], tiktokPixel: ['ttq.track', 'ttq.page', 'ttq.identify'], diff --git a/src/registry-types.json b/src/registry-types.json index 702874e3..0d72bb6d 100644 --- a/src/registry-types.json +++ b/src/registry-types.json @@ -577,6 +577,18 @@ "code": "interface RybbitQueueState {\n queue: Array<[string, ...any[]]>\n flushed: boolean\n}" } ], + "mixpanel-analytics": [ + { + "name": "MixpanelAnalyticsOptions", + "kind": "const", + "code": "export const MixpanelAnalyticsOptions = object({\n /**\n * Your Mixpanel project token.\n * @see https://docs.mixpanel.com/docs/tracking-methods/sdks/javascript#1-initialize-the-library\n */\n token: string(),\n})" + }, + { + "name": "MixpanelAnalyticsApi", + "kind": "interface", + "code": "export interface MixpanelAnalyticsApi {\n mixpanel: {\n track: (event: string, properties?: Record) => void\n identify: (distinctId: string) => void\n reset: () => void\n people: {\n set: (properties: Record) => void\n }\n register: (properties: Record) => void\n init: (token: string, config?: Record) => void\n }\n}" + } + ], "segment": [ { "name": "SegmentOptions", diff --git a/src/registry.ts b/src/registry.ts index ab6073b4..167a384c 100644 --- a/src/registry.ts +++ b/src/registry.ts @@ -122,6 +122,16 @@ export async function registry(resolve?: (path: string, opts?: ResolvePathOption from: await resolve('./runtime/registry/segment'), }, }, + { + label: 'Mixpanel', + src: 'https://cdn.mxpnl.com/libs/mixpanel-2-latest.min.js', + category: 'analytics', + logo: ``, + import: { + name: 'useScriptMixpanelAnalytics', + from: await resolve('./runtime/registry/mixpanel-analytics'), + }, + }, { label: 'Meta Pixel', proxy: 'metaPixel', diff --git a/src/runtime/registry/mixpanel-analytics.ts b/src/runtime/registry/mixpanel-analytics.ts new file mode 100644 index 00000000..4c52c89a --- /dev/null +++ b/src/runtime/registry/mixpanel-analytics.ts @@ -0,0 +1,69 @@ +import type { RegistryScriptInput } from '#nuxt-scripts/types' +import { useRegistryScript } from '../utils' +import { MixpanelAnalyticsOptions } from './schemas' + +export { MixpanelAnalyticsOptions } + +export type MixpanelAnalyticsInput = RegistryScriptInput + +export interface MixpanelAnalyticsApi { + mixpanel: { + track: (event: string, properties?: Record) => void + identify: (distinctId: string) => void + reset: () => void + people: { + set: (properties: Record) => void + } + register: (properties: Record) => void + init: (token: string, config?: Record) => void + } +} + +declare global { + interface Window { + mixpanel: MixpanelAnalyticsApi['mixpanel'] + } +} + +const methods = ['track', 'identify', 'reset', 'register'] as const +const peopleMethods = ['set'] as const + +export function useScriptMixpanelAnalytics(_options?: MixpanelAnalyticsInput) { + return useRegistryScript('mixpanelAnalytics', (options) => { + return { + scriptInput: { + src: 'https://cdn.mxpnl.com/libs/mixpanel-2-latest.min.js', + }, + schema: import.meta.dev ? MixpanelAnalyticsOptions : undefined, + scriptOptions: { + use() { + return { + mixpanel: window.mixpanel, + } + }, + }, + clientInit: import.meta.server + ? undefined + : () => { + window.mixpanel = window.mixpanel || [] as any + for (const method of methods) { + (window.mixpanel as any)[method] = (window.mixpanel as any)[method] || ((...args: any[]) => { + (window.mixpanel as any).push([method, ...args]) + }) + } + window.mixpanel.people = window.mixpanel.people || {} as any + for (const method of peopleMethods) { + (window.mixpanel.people as any)[method] = (window.mixpanel.people as any)[method] || ((...args: any[]) => { + (window.mixpanel as any).push([`people.${method}`, ...args]) + }) + } + window.mixpanel.init = window.mixpanel.init || ((...args: any[]) => { + (window.mixpanel as any).push(['init', ...args]) + }) + if (options?.token) { + window.mixpanel.init(options.token) + } + }, + } + }, _options) +} diff --git a/src/runtime/registry/schemas.ts b/src/runtime/registry/schemas.ts index 94c8b471..12687c48 100644 --- a/src/runtime/registry/schemas.ts +++ b/src/runtime/registry/schemas.ts @@ -716,6 +716,14 @@ export const RybbitAnalyticsOptions = object({ apiKey: optional(string()), }) +export const MixpanelAnalyticsOptions = object({ + /** + * Your Mixpanel project token. + * @see https://docs.mixpanel.com/docs/tracking-methods/sdks/javascript#1-initialize-the-library + */ + token: string(), +}) + export const SegmentOptions = object({ /** * Your Segment write key.