mixpanel-react-native 3.1.2 → 3.2.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. package/.claude/settings.local.json +12 -1
  2. package/.github/dependabot.yml +7 -0
  3. package/.github/workflows/node.js.yml +24 -1
  4. package/.vscode/settings.json +2 -1
  5. package/MixpanelReactNative.podspec +1 -1
  6. package/Samples/MixpanelExample/ios/MixpanelExample.xcworkspace/contents.xcworkspacedata +10 -0
  7. package/Samples/MixpanelExample/ios/Podfile.lock +1996 -0
  8. package/Samples/MixpanelStarter/.bundle/config +2 -0
  9. package/Samples/MixpanelStarter/.env.example +4 -0
  10. package/Samples/MixpanelStarter/.eslintrc.js +4 -0
  11. package/Samples/MixpanelStarter/.prettierrc.js +5 -0
  12. package/Samples/MixpanelStarter/.watchmanconfig +1 -0
  13. package/Samples/MixpanelStarter/App.tsx +10 -0
  14. package/Samples/MixpanelStarter/CLAUDE.md +538 -0
  15. package/Samples/MixpanelStarter/Gemfile +16 -0
  16. package/Samples/MixpanelStarter/INTEGRATION_GUIDE.md +606 -0
  17. package/Samples/MixpanelStarter/README.md +406 -0
  18. package/Samples/MixpanelStarter/__tests__/MixpanelContext.test.tsx +63 -0
  19. package/Samples/MixpanelStarter/android/app/build.gradle +119 -0
  20. package/Samples/MixpanelStarter/android/app/debug.keystore +0 -0
  21. package/Samples/MixpanelStarter/android/app/proguard-rules.pro +10 -0
  22. package/Samples/MixpanelStarter/android/app/src/main/AndroidManifest.xml +27 -0
  23. package/Samples/MixpanelStarter/android/app/src/main/java/com/mixpanelstarter/MainActivity.kt +22 -0
  24. package/Samples/MixpanelStarter/android/app/src/main/java/com/mixpanelstarter/MainApplication.kt +27 -0
  25. package/Samples/MixpanelStarter/android/app/src/main/res/drawable/rn_edit_text_material.xml +37 -0
  26. package/Samples/MixpanelStarter/android/app/src/main/res/mipmap-hdpi/ic_launcher.png +0 -0
  27. package/Samples/MixpanelStarter/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png +0 -0
  28. package/Samples/MixpanelStarter/android/app/src/main/res/mipmap-mdpi/ic_launcher.png +0 -0
  29. package/Samples/MixpanelStarter/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png +0 -0
  30. package/Samples/MixpanelStarter/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png +0 -0
  31. package/Samples/MixpanelStarter/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png +0 -0
  32. package/Samples/MixpanelStarter/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png +0 -0
  33. package/Samples/MixpanelStarter/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png +0 -0
  34. package/Samples/MixpanelStarter/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png +0 -0
  35. package/Samples/MixpanelStarter/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png +0 -0
  36. package/Samples/MixpanelStarter/android/app/src/main/res/values/strings.xml +3 -0
  37. package/Samples/MixpanelStarter/android/app/src/main/res/values/styles.xml +9 -0
  38. package/Samples/MixpanelStarter/android/build.gradle +21 -0
  39. package/Samples/MixpanelStarter/android/gradle/wrapper/gradle-wrapper.jar +0 -0
  40. package/Samples/MixpanelStarter/android/gradle/wrapper/gradle-wrapper.properties +7 -0
  41. package/Samples/MixpanelStarter/android/gradle.properties +44 -0
  42. package/Samples/MixpanelStarter/android/gradlew +251 -0
  43. package/Samples/MixpanelStarter/android/gradlew.bat +99 -0
  44. package/Samples/MixpanelStarter/android/settings.gradle +6 -0
  45. package/Samples/MixpanelStarter/app.json +4 -0
  46. package/Samples/MixpanelStarter/babel.config.js +14 -0
  47. package/Samples/MixpanelStarter/index.js +9 -0
  48. package/Samples/MixpanelStarter/ios/.xcode.env +11 -0
  49. package/Samples/MixpanelStarter/ios/MixpanelStarter/AppDelegate.swift +48 -0
  50. package/Samples/MixpanelStarter/ios/MixpanelStarter/Images.xcassets/AppIcon.appiconset/Contents.json +53 -0
  51. package/Samples/MixpanelStarter/ios/MixpanelStarter/Images.xcassets/Contents.json +6 -0
  52. package/Samples/MixpanelStarter/ios/MixpanelStarter/Info.plist +55 -0
  53. package/Samples/MixpanelStarter/ios/MixpanelStarter/LaunchScreen.storyboard +47 -0
  54. package/Samples/MixpanelStarter/ios/MixpanelStarter/PrivacyInfo.xcprivacy +38 -0
  55. package/Samples/MixpanelStarter/ios/MixpanelStarter.xcodeproj/project.pbxproj +482 -0
  56. package/Samples/MixpanelStarter/ios/MixpanelStarter.xcodeproj/xcshareddata/xcschemes/MixpanelStarter.xcscheme +88 -0
  57. package/Samples/MixpanelStarter/ios/MixpanelStarter.xcworkspace/contents.xcworkspacedata +10 -0
  58. package/Samples/MixpanelStarter/ios/Podfile +34 -0
  59. package/Samples/MixpanelStarter/ios/Podfile.lock +2839 -0
  60. package/Samples/MixpanelStarter/jest.config.js +3 -0
  61. package/Samples/MixpanelStarter/metro.config.js +42 -0
  62. package/Samples/MixpanelStarter/package-lock.json +12141 -0
  63. package/Samples/MixpanelStarter/package.json +51 -0
  64. package/Samples/MixpanelStarter/src/@types/env.d.ts +3 -0
  65. package/Samples/MixpanelStarter/src/App.tsx +83 -0
  66. package/Samples/MixpanelStarter/src/components/ActionButton.tsx +92 -0
  67. package/Samples/MixpanelStarter/src/components/ErrorBoundary.tsx +81 -0
  68. package/Samples/MixpanelStarter/src/components/EventTrackingLog.tsx +163 -0
  69. package/Samples/MixpanelStarter/src/components/FlagCard.tsx +199 -0
  70. package/Samples/MixpanelStarter/src/components/InfoCard.tsx +77 -0
  71. package/Samples/MixpanelStarter/src/components/TestResultDisplay.tsx +181 -0
  72. package/Samples/MixpanelStarter/src/constants/tracking.ts +77 -0
  73. package/Samples/MixpanelStarter/src/contexts/MixpanelContext.tsx +159 -0
  74. package/Samples/MixpanelStarter/src/screens/FeatureFlagsScreen.tsx +1011 -0
  75. package/Samples/MixpanelStarter/src/screens/HomeScreen.tsx +307 -0
  76. package/Samples/MixpanelStarter/src/screens/OnboardingScreen.tsx +253 -0
  77. package/Samples/MixpanelStarter/src/screens/SettingsScreen.tsx +316 -0
  78. package/Samples/MixpanelStarter/src/types/flags.types.ts +42 -0
  79. package/Samples/MixpanelStarter/src/types/mixpanel.types.ts +26 -0
  80. package/Samples/MixpanelStarter/tsconfig.json +13 -0
  81. package/__tests__/flags.test.js +730 -0
  82. package/__tests__/index.test.js +7 -3
  83. package/__tests__/jest_setup.js +18 -0
  84. package/android/build.gradle +1 -1
  85. package/android/src/main/java/com/mixpanel/reactnative/MixpanelReactNativeModule.java +272 -2
  86. package/index.d.ts +64 -1
  87. package/index.js +42 -3
  88. package/ios/MixpanelReactNative.m +19 -1
  89. package/ios/MixpanelReactNative.swift +183 -5
  90. package/javascript/mixpanel-flags-js.js +463 -0
  91. package/javascript/mixpanel-flags.js +290 -0
  92. package/javascript/mixpanel-main.js +13 -1
  93. package/package.json +2 -2
@@ -0,0 +1,606 @@
1
+ # Integration Guide: Adding Mixpanel to Your React Native App
2
+
3
+ This guide provides step-by-step instructions for integrating Mixpanel into your React Native application using the patterns demonstrated in MixpanelStarter.
4
+
5
+ ## Prerequisites Checklist
6
+
7
+ Before you begin, ensure you have:
8
+
9
+ - [ ] React Native app (0.70+) with TypeScript
10
+ - [ ] Mixpanel project token ([create one](https://mixpanel.com/register))
11
+ - [ ] `@react-native-async-storage/async-storage` installed (required dependency)
12
+
13
+ ## 8-Step Integration Process
14
+
15
+ ### Step 1: Install Dependencies
16
+
17
+ ```bash
18
+ npm install mixpanel-react-native @react-native-async-storage/async-storage
19
+ ```
20
+
21
+ For iOS, install pods:
22
+
23
+ ```bash
24
+ cd ios && pod install && cd ..
25
+ ```
26
+
27
+ ### Step 2: Set Up Environment Variables (Optional but Recommended)
28
+
29
+ Install dotenv support:
30
+
31
+ ```bash
32
+ npm install react-native-dotenv --save-dev
33
+ ```
34
+
35
+ Configure `babel.config.js`:
36
+
37
+ ```javascript
38
+ module.exports = {
39
+ presets: ['module:@react-native/babel-preset'],
40
+ plugins: [
41
+ [
42
+ 'module:react-native-dotenv',
43
+ {
44
+ moduleName: '@env',
45
+ path: '.env',
46
+ safe: false,
47
+ allowUndefined: true,
48
+ },
49
+ ],
50
+ ],
51
+ };
52
+ ```
53
+
54
+ Create `.env`:
55
+
56
+ ```bash
57
+ MIXPANEL_TOKEN=your_token_here
58
+ ```
59
+
60
+ Create type definitions in `src/@types/env.d.ts`:
61
+
62
+ ```typescript
63
+ declare module '@env' {
64
+ export const MIXPANEL_TOKEN: string;
65
+ }
66
+ ```
67
+
68
+ ### Step 3: Define Event Constants
69
+
70
+ Create `src/constants/tracking.ts`:
71
+
72
+ ```typescript
73
+ // Event Names
74
+ export const Events = {
75
+ // Screen Views
76
+ SCREEN_VIEWED: 'Screen Viewed',
77
+
78
+ // User Actions
79
+ USER_SIGNED_UP: 'User Signed Up',
80
+ USER_LOGGED_IN: 'User Logged In',
81
+ USER_LOGGED_OUT: 'User Logged Out',
82
+
83
+ // Add your app-specific events here
84
+ } as const;
85
+
86
+ // Property Names
87
+ export const Properties = {
88
+ SCREEN_NAME: 'screen_name',
89
+ USER_ID: 'user_id',
90
+ TIMESTAMP: 'timestamp',
91
+
92
+ // Add your app-specific properties here
93
+ } as const;
94
+
95
+ // Super Property Keys
96
+ export const SuperProperties = {
97
+ APP_VERSION: 'App Version',
98
+ PLATFORM: 'Platform',
99
+ ENVIRONMENT: 'Environment',
100
+ } as const;
101
+ ```
102
+
103
+ **Why?** Constants prevent typos, enable autocomplete, and make refactoring easier.
104
+
105
+ ### Step 4: Create Type Definitions
106
+
107
+ Create `src/types/mixpanel.types.ts`:
108
+
109
+ ```typescript
110
+ import {Mixpanel} from 'mixpanel-react-native';
111
+
112
+ export interface MixpanelContextValue {
113
+ mixpanel: Mixpanel | null;
114
+ isInitialized: boolean;
115
+ isLoading: boolean;
116
+ error: Error | null;
117
+
118
+ // Convenience methods
119
+ track: (eventName: string, properties?: Record<string, any>) => void;
120
+ identify: (distinctId: string) => void;
121
+ alias: (alias: string, distinctId?: string) => void;
122
+ reset: () => void;
123
+ flush: () => Promise<void>;
124
+ }
125
+ ```
126
+
127
+ ### Step 5: Create Mixpanel Context
128
+
129
+ Create `src/contexts/MixpanelContext.tsx`:
130
+
131
+ ```typescript
132
+ import React, {
133
+ createContext,
134
+ useContext,
135
+ useEffect,
136
+ useState,
137
+ useCallback,
138
+ ReactNode,
139
+ } from 'react';
140
+ import {Mixpanel} from 'mixpanel-react-native';
141
+ import {Platform} from 'react-native';
142
+ import {MixpanelContextValue} from '../types/mixpanel.types';
143
+ import {SuperProperties} from '../constants/tracking';
144
+
145
+ const MixpanelContext = createContext<MixpanelContextValue | undefined>(
146
+ undefined,
147
+ );
148
+
149
+ interface MixpanelProviderProps {
150
+ children: ReactNode;
151
+ token: string;
152
+ trackAutomaticEvents?: boolean;
153
+ useNative?: boolean;
154
+ }
155
+
156
+ export const MixpanelProvider: React.FC<MixpanelProviderProps> = ({
157
+ children,
158
+ token,
159
+ trackAutomaticEvents = true,
160
+ useNative = true,
161
+ }) => {
162
+ const [mixpanel, setMixpanel] = useState<Mixpanel | null>(null);
163
+ const [isInitialized, setIsInitialized] = useState(false);
164
+ const [isLoading, setIsLoading] = useState(true);
165
+ const [error, setError] = useState<Error | null>(null);
166
+
167
+ useEffect(() => {
168
+ const initMixpanel = async () => {
169
+ try {
170
+ setIsLoading(true);
171
+ setError(null);
172
+
173
+ const instance = new Mixpanel(token, trackAutomaticEvents, useNative);
174
+ await instance.init();
175
+
176
+ // Set default super properties
177
+ instance.registerSuperProperties({
178
+ [SuperProperties.APP_VERSION]: '1.0.0', // Replace with actual version
179
+ [SuperProperties.PLATFORM]: Platform.OS,
180
+ [SuperProperties.ENVIRONMENT]: __DEV__ ? 'development' : 'production',
181
+ });
182
+
183
+ if (__DEV__) {
184
+ instance.setLoggingEnabled(true);
185
+ }
186
+
187
+ setMixpanel(instance);
188
+ setIsInitialized(true);
189
+ } catch (err) {
190
+ const error = err instanceof Error ? err : new Error(String(err));
191
+ setError(error);
192
+ console.error('Failed to initialize Mixpanel:', error);
193
+ } finally {
194
+ setIsLoading(false);
195
+ }
196
+ };
197
+
198
+ initMixpanel();
199
+ }, [token, trackAutomaticEvents, useNative]);
200
+
201
+ // Convenience wrapper methods
202
+ const track = useCallback(
203
+ (eventName: string, properties?: Record<string, any>) => {
204
+ if (!mixpanel || !isInitialized) {
205
+ console.warn('Mixpanel not initialized. Event:', eventName);
206
+ return;
207
+ }
208
+ mixpanel.track(eventName, properties);
209
+ },
210
+ [mixpanel, isInitialized],
211
+ );
212
+
213
+ const identify = useCallback(
214
+ (distinctId: string) => {
215
+ if (!mixpanel || !isInitialized) return;
216
+ mixpanel.identify(distinctId);
217
+ },
218
+ [mixpanel, isInitialized],
219
+ );
220
+
221
+ const alias = useCallback(
222
+ (aliasValue: string, distinctId?: string) => {
223
+ if (!mixpanel || !isInitialized) return;
224
+ mixpanel.alias(aliasValue, distinctId);
225
+ },
226
+ [mixpanel, isInitialized],
227
+ );
228
+
229
+ const reset = useCallback(() => {
230
+ if (!mixpanel || !isInitialized) return;
231
+ mixpanel.reset();
232
+ }, [mixpanel, isInitialized]);
233
+
234
+ const flush = useCallback(async () => {
235
+ if (!mixpanel || !isInitialized) return;
236
+ await mixpanel.flush();
237
+ }, [mixpanel, isInitialized]);
238
+
239
+ const value: MixpanelContextValue = {
240
+ mixpanel,
241
+ isInitialized,
242
+ isLoading,
243
+ error,
244
+ track,
245
+ identify,
246
+ alias,
247
+ reset,
248
+ flush,
249
+ };
250
+
251
+ return (
252
+ <MixpanelContext.Provider value={value}>
253
+ {children}
254
+ </MixpanelContext.Provider>
255
+ );
256
+ };
257
+
258
+ export const useMixpanel = (): MixpanelContextValue => {
259
+ const context = useContext(MixpanelContext);
260
+ if (context === undefined) {
261
+ throw new Error('useMixpanel must be used within a MixpanelProvider');
262
+ }
263
+ return context;
264
+ };
265
+ ```
266
+
267
+ ### Step 6: Wrap Your App Root
268
+
269
+ In your root `App.tsx`:
270
+
271
+ ```typescript
272
+ import {MixpanelProvider} from './src/contexts/MixpanelContext';
273
+ import {MIXPANEL_TOKEN} from '@env';
274
+
275
+ function App() {
276
+ return (
277
+ <MixpanelProvider token={MIXPANEL_TOKEN} trackAutomaticEvents={true}>
278
+ {/* Your app content */}
279
+ </MixpanelProvider>
280
+ );
281
+ }
282
+ ```
283
+
284
+ ### Step 7: Use in Components
285
+
286
+ Track events in any component:
287
+
288
+ ```typescript
289
+ import {useMixpanel} from '../contexts/MixpanelContext';
290
+ import {Events, Properties} from '../constants/tracking';
291
+
292
+ function MyScreen() {
293
+ const {track, isInitialized} = useMixpanel();
294
+
295
+ useEffect(() => {
296
+ if (isInitialized) {
297
+ track(Events.SCREEN_VIEWED, {
298
+ [Properties.SCREEN_NAME]: 'MyScreen',
299
+ [Properties.TIMESTAMP]: new Date().toISOString(),
300
+ });
301
+ }
302
+ }, [isInitialized, track]);
303
+
304
+ const handleButtonClick = () => {
305
+ track('Button Clicked', {
306
+ button_name: 'Submit',
307
+ screen: 'MyScreen',
308
+ });
309
+ };
310
+
311
+ return (
312
+ // Your UI
313
+ );
314
+ }
315
+ ```
316
+
317
+ ### Step 8: Test Your Integration
318
+
319
+ 1. **Enable Logging** (in development):
320
+ ```typescript
321
+ // Already enabled in context if __DEV__ is true
322
+ ```
323
+
324
+ 2. **Check Console**: Look for `[Mixpanel]` logs showing events being tracked
325
+
326
+ 3. **Verify in Dashboard**:
327
+ - Go to Mixpanel dashboard
328
+ - Navigate to "Events" or "Live View"
329
+ - Confirm events are arriving
330
+
331
+ 4. **Test Key Flows**:
332
+ ```typescript
333
+ // Test user identification
334
+ identify('user123');
335
+
336
+ // Test event tracking
337
+ track('Test Event', {test: true});
338
+
339
+ // Test manual flush
340
+ await flush();
341
+ ```
342
+
343
+ ## Testing Validation Checklist
344
+
345
+ - [ ] Events appear in Mixpanel dashboard
346
+ - [ ] `getDistinctId()` returns a valid ID
347
+ - [ ] Console shows `[Mixpanel]` logs in development
348
+ - [ ] User profiles appear after `identify()` + `getPeople().set()`
349
+ - [ ] Super properties are included in all events
350
+
351
+ ## Best Practices
352
+
353
+ ### Event Naming Conventions
354
+
355
+ ```typescript
356
+ // ✅ Good: Noun + Verb (Past Tense)
357
+ 'Product Viewed'
358
+ 'Purchase Completed'
359
+ 'Video Started'
360
+
361
+ // ❌ Bad: Vague or inconsistent
362
+ 'click'
363
+ 'ProductView'
364
+ 'video_start'
365
+ ```
366
+
367
+ ### Property Naming Conventions
368
+
369
+ ```typescript
370
+ // ✅ Good: snake_case, descriptive
371
+ {
372
+ product_id: 'prod-123',
373
+ product_name: 'Sample Product',
374
+ product_price: 29.99,
375
+ }
376
+
377
+ // ❌ Bad: Inconsistent casing
378
+ {
379
+ ProductID: 'prod-123',
380
+ 'product name': 'Sample Product',
381
+ price: 29.99,
382
+ }
383
+ ```
384
+
385
+ ### Code Organization
386
+
387
+ ```
388
+ your-app/
389
+ ├── src/
390
+ │ ├── contexts/
391
+ │ │ └── MixpanelContext.tsx # Keep in contexts/
392
+ │ ├── constants/
393
+ │ │ └── tracking.ts # Centralize event definitions
394
+ │ └── screens/
395
+ │ └── MyScreen.tsx # Import useMixpanel() as needed
396
+ ```
397
+
398
+ ### When to Track
399
+
400
+ **✅ Track:**
401
+ - User actions (button clicks, form submissions)
402
+ - Navigation (screen views, tab switches)
403
+ - Key conversions (signup, purchase, subscription)
404
+ - Feature usage (search, filter, share)
405
+
406
+ **❌ Don't Track:**
407
+ - Every render or state change
408
+ - Internal errors (use error monitoring instead)
409
+ - PII without consent (email, phone without user permission)
410
+
411
+ ## Common Pitfalls
412
+
413
+ ### 1. Tracking Before Initialization
414
+
415
+ ```typescript
416
+ // ❌ Bad
417
+ track('Event'); // Might not be initialized yet
418
+
419
+ // ✅ Good
420
+ if (isInitialized) {
421
+ track('Event');
422
+ }
423
+ ```
424
+
425
+ ### 2. Forgetting to Alias
426
+
427
+ ```typescript
428
+ // ❌ Bad: User's anonymous events are lost
429
+ identify(userId);
430
+
431
+ // ✅ Good: Preserve event history
432
+ const previousId = await mixpanel.getDistinctId();
433
+ identify(userId);
434
+ alias(userId, previousId);
435
+ ```
436
+
437
+ ### 3. Not Flushing Before Logout
438
+
439
+ ```typescript
440
+ // ❌ Bad: Queued events are lost
441
+ reset();
442
+
443
+ // ✅ Good: Send events before clearing
444
+ track('User Logged Out');
445
+ await flush();
446
+ reset();
447
+ ```
448
+
449
+ ### 4. Hardcoding Event Names
450
+
451
+ ```typescript
452
+ // ❌ Bad: Typos cause tracking issues
453
+ track('Screen Viewd'); // Typo!
454
+
455
+ // ✅ Good: Use constants
456
+ track(Events.SCREEN_VIEWED);
457
+ ```
458
+
459
+ ### 5. Over-Tracking
460
+
461
+ ```typescript
462
+ // ❌ Bad: Too noisy
463
+ onChange={(text) => track('Input Changed', {text})}
464
+
465
+ // ✅ Good: Track meaningful actions
466
+ onSubmit={() => track('Form Submitted', {form: 'login'})}
467
+ ```
468
+
469
+ ## Essential Methods Reference
470
+
471
+ | Method | Purpose | Example |
472
+ |--------|---------|---------|
473
+ | `track()` | Send an event | `track('Button Clicked', {button: 'Submit'})` |
474
+ | `identify()` | Set user ID | `identify('user123')` |
475
+ | `alias()` | Link IDs | `alias('user123', 'anon-456')` |
476
+ | `getPeople().set()` | Set profile property | `getPeople().set({plan: 'premium'})` |
477
+ | `registerSuperProperties()` | Set global property | `registerSuperProperties({theme: 'dark'})` |
478
+ | `reset()` | Clear all data | `reset()` // Call on logout |
479
+ | `flush()` | Send queued events | `await flush()` // Before logout |
480
+ | `optInTracking()` | Enable tracking | `await optInTracking()` |
481
+ | `optOutTracking()` | Disable tracking | `await optOutTracking()` |
482
+ | `getDistinctId()` | Get current ID | `const id = await getDistinctId()` |
483
+
484
+ ## Advanced Patterns
485
+
486
+ ### User Identification Flow
487
+
488
+ ```typescript
489
+ const handleLogin = async (email: string, password: string) => {
490
+ // 1. Authenticate user (your auth logic)
491
+ const user = await authenticate(email, password);
492
+
493
+ // 2. Get previous anonymous ID
494
+ const previousId = await mixpanel.getDistinctId();
495
+
496
+ // 3. Identify with user ID
497
+ identify(user.id);
498
+
499
+ // 4. Link anonymous events to this user
500
+ alias(user.id, previousId);
501
+
502
+ // 5. Set user profile
503
+ mixpanel.getPeople().set({
504
+ $email: user.email,
505
+ $name: user.name,
506
+ plan: user.plan,
507
+ });
508
+
509
+ // 6. Track login event
510
+ track(Events.USER_LOGGED_IN, {
511
+ method: 'email',
512
+ timestamp: new Date().toISOString(),
513
+ });
514
+ };
515
+ ```
516
+
517
+ ### GDPR-Compliant Logout
518
+
519
+ ```typescript
520
+ const handleLogout = async () => {
521
+ // 1. Track logout event
522
+ track(Events.USER_LOGGED_OUT, {
523
+ timestamp: new Date().toISOString(),
524
+ });
525
+
526
+ // 2. Flush queued events
527
+ await flush();
528
+
529
+ // 3. Clear all Mixpanel data
530
+ reset();
531
+
532
+ // 4. Your app logout logic
533
+ await signOut();
534
+ };
535
+ ```
536
+
537
+ ### Timed Events
538
+
539
+ ```typescript
540
+ const handleVideoStart = () => {
541
+ // Start timer
542
+ mixpanel.timeEvent('Video Completed');
543
+
544
+ track('Video Started', {
545
+ video_id: 'intro-123',
546
+ video_title: 'Introduction',
547
+ });
548
+ };
549
+
550
+ const handleVideoEnd = () => {
551
+ // Duration is automatically calculated
552
+ track('Video Completed', {
553
+ video_id: 'intro-123',
554
+ video_title: 'Introduction',
555
+ });
556
+ };
557
+ ```
558
+
559
+ ## Troubleshooting
560
+
561
+ ### Events Not Showing Up
562
+
563
+ 1. Check token: `console.log(MIXPANEL_TOKEN)`
564
+ 2. Verify initialization: `console.log(isInitialized)`
565
+ 3. Check opt-out status: `const hasOptedOut = await mixpanel.hasOptedOutTracking()`
566
+ 4. Enable logging: Already enabled if `__DEV__` is true
567
+ 5. Manual flush: `await flush()`
568
+
569
+ ### TypeScript Errors
570
+
571
+ ```bash
572
+ # Clear Metro cache
573
+ npm start -- --reset-cache
574
+
575
+ # Reinstall pods (iOS)
576
+ cd ios && pod install && cd ..
577
+ ```
578
+
579
+ ### Build Issues
580
+
581
+ ```bash
582
+ # iOS
583
+ cd ios && xcodebuild clean && cd ..
584
+
585
+ # Android
586
+ cd android && ./gradlew clean && cd ..
587
+ ```
588
+
589
+ ## Next Steps
590
+
591
+ 1. ✅ Complete the 8-step integration
592
+ 2. ✅ Validate with the testing checklist
593
+ 3. 📊 Define your tracking plan (which events, which properties)
594
+ 4. 🎨 Customize super properties for your app
595
+ 5. 🧪 Test in development before production
596
+ 6. 📈 Monitor your Mixpanel dashboard
597
+
598
+ ## Resources
599
+
600
+ - [Mixpanel React Native SDK Docs](https://docs.mixpanel.com/docs/tracking/advanced/react-native)
601
+ - [Mixpanel Best Practices](https://docs.mixpanel.com/docs/tracking/how-tos/events-and-properties)
602
+ - [MixpanelStarter Sample Code](./src)
603
+
604
+ ---
605
+
606
+ **Questions?** Check the [README](./README.md) or [Mixpanel Community](https://community.mixpanel.com/).