@jvidalv/react-analytics
v1.0.4
Published
A universal analytics library for any React application (web, React Native, Expo, Next.js). Plug and play with automatic navigation tracking, built-in storage adapters, and support for multiple routers.
Maintainers
Readme
React Analytics
A universal analytics library that works with any React project - Next.js (App/Pages Router), Expo, React Native, React Router, or plain React. Plug and play with automatic navigation tracking, built-in storage adapters, and zero configuration for most frameworks.
Features
- 📱 Universal Compatibility - Works seamlessly with Next.js, Expo, React Native, React Router, or any React app
- 🚀 Zero Config Setup - Drop in
<AnalyticsProvider>and start tracking immediately - 🔄 Auto Navigation Tracking - Automatically detects and tracks route changes across all routers
- 🎯 Smart Router Detection - Auto-detects expo-router, react-router, Next.js App/Pages Router
- 💾 Built-in Storage - Uses localStorage for web, AsyncStorage for React Native (auto-detected)
- 📊 Event Batching - Queues and pushes events every 5 seconds with retry logic
- 💬 Contact Form Messages - Built-in message tracking with dashboard management
- 🛡️ Error Boundary - Automatic error reporting with React error boundaries
- 🔒 Type-Safe - Full TypeScript support with comprehensive types
Installation
npm install @jvidalv/react-analytics
# or
yarn add @jvidalv/react-analytics
# or
pnpm add @jvidalv/react-analyticsPlatform-Specific Dependencies
For React Native / Expo:
npm install @react-native-async-storage/async-storageOptional (for enhanced device info in React Native):
npx expo install expo-device expo-application expo-constantsQuick Start
Option 1: Provider Component (Recommended)
The declarative approach - perfect for most React applications. Just wrap your app with <AnalyticsProvider>:
import { AnalyticsProvider, analytics } from "@jvidalv/react-analytics";
function App() {
return (
<AnalyticsProvider config={{ apiKey: "your-api-key" }}>
<YourApp />
</AnalyticsProvider>
);
}
// Track events anywhere in your app
analytics.identify("user-123", { email: "[email protected]" });
analytics.action("button_click", { button: "login" });
analytics.error("Network error", { statusCode: 500 });
analytics.message("[email protected]", "I need help with...");The provider automatically:
- ✅ Initializes analytics on mount
- ✅ Auto-detects your router and tracks navigation
- ✅ Handles cleanup on unmount
- ✅ Works with Next.js SSR/SSG (client-side only)
Option 2: Manual Init (Advanced)
For edge cases where you need granular control over initialization:
import { analytics } from "@jvidalv/react-analytics";
// Initialize manually at app startup
await analytics.init({
apiKey: "your-api-key",
debug: true, // Optional
});
// Track events and navigation manually
analytics.page("/dashboard");
analytics.action("button_click", { button: "login" });Note: With manual init, you'll need to track navigation yourself unless you use
<RouterTracker>. See Advanced Usage for details.
Platform Examples
Next.js App Router
// app/layout.tsx
import { AnalyticsProvider } from "@jvidalv/react-analytics";
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body>
<AnalyticsProvider
config={{ apiKey: process.env.NEXT_PUBLIC_ANALYTICS_KEY! }}
>
{children}
</AnalyticsProvider>
</body>
</html>
);
}Note:
AnalyticsProvideris a client component and works seamlessly with Next.js App Router. Analytics only run client-side, making it safe for SSR.
Next.js Pages Router (Legacy)
For projects still using Pages Router:
// pages/_app.tsx
import type { AppProps } from "next/app";
import { AnalyticsProvider } from "@jvidalv/react-analytics";
export default function App({ Component, pageProps }: AppProps) {
return (
<AnalyticsProvider
config={{ apiKey: process.env.NEXT_PUBLIC_ANALYTICS_KEY! }}
>
<Component {...pageProps} />
</AnalyticsProvider>
);
}React Native with Expo Router
// app/_layout.tsx
import { Stack } from "expo-router";
import { AnalyticsProvider } from "@jvidalv/react-analytics";
export default function RootLayout() {
return (
<AnalyticsProvider
config={{ apiKey: process.env.EXPO_PUBLIC_ANALYTICS_KEY }}
>
<Stack />
</AnalyticsProvider>
);
}React SPA with React Router
import { createRoot } from "react-dom/client";
import { BrowserRouter } from "react-router-dom";
import { AnalyticsProvider } from "@jvidalv/react-analytics";
import App from "./App";
createRoot(document.getElementById("root")).render(
<AnalyticsProvider config={{ apiKey: process.env.REACT_APP_ANALYTICS_KEY }}>
<BrowserRouter>
<App />
</BrowserRouter>
</AnalyticsProvider>,
);Plain React (Any Setup)
For vanilla React apps without a specific router, or when you need manual control:
// App.tsx
import { useEffect } from "react";
import { analytics } from "@jvidalv/react-analytics";
export default function App() {
useEffect(() => {
analytics.init({
apiKey: "your-api-key",
});
}, []);
return <YourApp />;
}Configuration
type AnalyticsConfig = {
apiKey: string; // Required: Your analytics API key
url?: string; // Optional: Custom API endpoint
appVersion?: string; // Optional: App version (auto-detected)
debug?: boolean; // Optional: Enable console logging
events?: {
disableNavigationEvents?: boolean; // Disable auto navigation tracking
disableStateEvents?: boolean; // Disable app state tracking (RN only)
};
};API Methods
analytics.init(config)
Initialize the analytics library. Must be called before tracking events.
await analytics.init({
apiKey: "your-api-key",
debug: true,
});analytics.identify(userId, properties?)
Identify a user and optionally set user properties.
analytics.identify("user-123", {
email: "[email protected]",
firstName: "John",
lastName: "Doe",
avatarUrl: "https://example.com/avatar.jpg",
plan: "premium",
});analytics.action(name, properties?)
Track a custom action/event.
analytics.action("button_click", {
button: "signup",
location: "header",
});
analytics.action("purchase", {
productId: "prod-123",
amount: 29.99,
currency: "USD",
});analytics.error(message, properties?)
Track an error.
analytics.error("Payment failed", {
errorCode: "CARD_DECLINED",
amount: 99.99,
});analytics.page(path, properties?)
Manually track a page view. Useful when automatic tracking is disabled or for custom navigation.
analytics.page("/dashboard", {
section: "analytics",
});analytics.message(contact, content)
Track a contact form message. Messages appear in your dashboard's Messages section where you can view, respond, and manage them.
analytics.message("[email protected]", "I need help with my account settings.");
analytics.message("+1234567890", "Please call me back regarding my order.");Dashboard Features:
- View all messages with contact info, platform, and country
- Mark messages as "seen" or "completed"
- Add private notes to messages
- Filter by status (new, seen, completed)
- Deep-link to specific messages via URL
analytics.cleanup()
Stop tracking and cleanup resources. Usually not needed unless you want to manually disable analytics.
analytics.cleanup();Event Types
The library automatically tracks and sends these event types:
Navigation Event
{
type: 'navigation',
path: '/dashboard',
date: '2025-01-04T12:00:00.000Z',
properties?: {
params?: { id: '123' }
}
}Action Event
{
type: 'action',
name: 'button_click',
date: '2025-01-04T12:00:00.000Z',
properties?: { button: 'signup' }
}Identify Event
{
type: 'identify',
id: 'user-123',
date: '2025-01-04T12:00:00.000Z',
properties?: {
email: '[email protected]',
firstName: 'John'
}
}Error Event
{
type: 'error',
message: 'Payment failed',
date: '2025-01-04T12:00:00.000Z',
properties?: {
message: 'Full error message',
errorCode: 'CARD_DECLINED'
}
}Message Event
{
type: 'message',
date: '2025-01-04T12:00:00.000Z',
properties: {
contact: '[email protected]',
content: 'I need help with my account'
}
}State Event (React Native only)
{
type: 'state',
active: true, // or false
date: '2025-01-04T12:00:00.000Z'
}Error Boundary
The AnalyticsErrorBoundary component automatically catches React rendering errors and reports them to analytics with rich context including stack traces, component stack, route, and device info.
Basic Usage
import { AnalyticsProvider, AnalyticsErrorBoundary } from "@jvidalv/react-analytics";
function App() {
return (
<AnalyticsProvider config={{ apiKey: "your-api-key" }}>
<AnalyticsErrorBoundary>
<MyApp />
</AnalyticsErrorBoundary>
</AnalyticsProvider>
);
}Custom Fallback UI
<AnalyticsErrorBoundary
fallback={(error, reset) => (
<div>
<h1>Something went wrong</h1>
<p>{error.message}</p>
<button onClick={reset}>Try again</button>
</div>
)}
>
<MyApp />
</AnalyticsErrorBoundary>With Error Callback
<AnalyticsErrorBoundary
onError={(error, errorInfo) => {
// Custom logging, Sentry integration, etc.
console.error("Caught error:", error);
console.error("Component stack:", errorInfo.componentStack);
}}
>
<MyApp />
</AnalyticsErrorBoundary>Props
| Prop | Type | Description |
|------|------|-------------|
| children | ReactNode | The components to wrap |
| fallback | ReactNode \| ((error: Error, reset: () => void) => ReactNode) | UI to show when an error occurs. Can be a static element or a render function with access to the error and reset function. Defaults to null for universal compatibility. |
| onError | (error: Error, errorInfo: ErrorInfo) => void | Optional callback when an error is caught |
| disabled | boolean | Set to true to disable analytics reporting (still catches errors) |
Error Data Captured
When an error is caught, the following data is automatically sent to analytics:
{
type: 'error',
message: 'Error message here',
properties: {
name: 'TypeError', // Error name
stack: '...', // Stack trace (limited to 2000 chars)
componentStack: '...', // React component stack (limited to 2000 chars)
route: '/current/path', // Current route (web only)
platform: 'web', // Platform (web/ios/android)
osVersion: '14.0', // OS version
browser: 'Chrome', // Browser (web only)
source: 'ErrorBoundary' // Identifies the error source
}
}Advanced Usage
Manual Initialization (Without Provider)
For apps that need fine-grained control over initialization timing or lifecycle:
import { analytics } from "@jvidalv/react-analytics";
import { useEffect } from "react";
function App() {
useEffect(() => {
// Initialize manually
analytics.init({
apiKey: "your-api-key",
debug: true,
appVersion: "1.2.3",
events: {
disableNavigationEvents: false,
disableStateEvents: false,
},
});
// Cleanup when component unmounts
return () => {
analytics.cleanup();
};
}, []);
return <YourApp />;
}When to use manual init:
- You need to initialize analytics conditionally (e.g., based on user consent)
- You're using a custom router not supported by auto-detection
- You want to control exactly when tracking starts
- Your app architecture doesn't support provider patterns
When to use AnalyticsProvider instead:
- Most standard React apps (Next.js, Expo, React Router, etc.)
- You want automatic router tracking
- You prefer declarative React patterns
Manual Router Tracking
If you've manually initialized and want to add router tracking:
import { RouterTracker, navigation } from "@jvidalv/react-analytics";
function App() {
useEffect(() => {
analytics.init({ apiKey: "your-api-key" });
}, []);
return (
<>
<RouterTracker onNavigate={navigation} />
<YourApp />
</>
);
}Disable Automatic Tracking
// Disable navigation tracking
<AnalyticsProvider
config={{
apiKey: "your-api-key",
events: {
disableNavigationEvents: true, // No auto navigation tracking
disableStateEvents: true, // No app state tracking (RN only)
},
}}
>
<App />
</AnalyticsProvider>Custom Router Trackers
Use specific router trackers if needed:
import {
ExpoRouterTracker,
ReactRouterTracker,
NextAppRouterTracker,
NextPagesRouterTracker,
} from "@jvidalv/react-analytics";
// Use a specific tracker
<ExpoRouterTracker onNavigate={navigation} />;How It Works
Initialization: When you call
analytics.init()or use<AnalyticsProvider>, the library:- Detects the platform (web or React Native)
- Sets up appropriate storage (localStorage or AsyncStorage)
- Generates or retrieves a persistent anonymous user ID
- Collects device information
- Starts the event push queue
Event Queue: Events are stored in memory and automatically pushed to the API every 5 seconds. If the push fails, the library retries with exponential backoff.
Router Detection: The library automatically detects which router you're using and tracks navigation changes. Supported routers:
- expo-router (Expo apps)
- react-router-dom v6+ (React SPAs)
- next/router (Next.js Pages Router)
- next/navigation (Next.js App Router)
Storage:
- Web: Uses
localStorage(no installation needed) - React Native: Uses
@react-native-async-storage/async-storage(must be installed) - Fallback: In-memory storage if neither is available
- Web: Uses
Troubleshooting
"AsyncStorage not found" warning (React Native)
Install the peer dependency:
npm install @react-native-async-storage/async-storageNavigation not tracking automatically
- Make sure you're using a supported router
- Check that
disableNavigationEventsis not set totrue - Try manual tracking with
analytics.page(path)
Events not appearing in dashboard
- Check your API key is correct
- Enable
debug: truein config to see console logs - Check network requests in dev tools
- Ensure you're calling
analytics.init()before tracking events
License
ISC
Support
For issues and feature requests, please visit: https://github.com/jvidalv/react-analytics
