npm package: @pulsora/react
Description: React hooks for Pulsora Analytics - Privacy-first analytics tracking
Author: Pulsora.co
Latest version: 1.0.0

# @pulsora/react

React hooks for Pulsora Analytics. Privacy-first analytics tracking with idiomatic React integration.

## Installation

```bash
npm install @pulsora/react @pulsora/core
```

## Quick Start

```tsx
import { PulsoraProvider, usePageview, useEvent } from '@pulsora/react';

function App() {
  return (
    <PulsoraProvider config={{ apiToken: 'your-api-token' }}>
      <YourApp />
    </PulsoraProvider>
  );
}

function MyPage() {
  usePageview(); // Automatic pageview tracking
  const trackEvent = useEvent();

  return (
    <button onClick={() => trackEvent('button_click', { value: 'cta' })}>
      Click Me
    </button>
  );
}
```

## API Reference

### `<PulsoraProvider>`

Context provider that initializes Pulsora and makes it available to child components.

#### Props

```typescript
interface PulsoraProviderProps {
  config: PulsoraConfig;
  children: ReactNode;
}

interface PulsoraConfig {
  apiToken: string;
  endpoint?: string; // Default: https://api.pulsora.co/api/ingest
  debug?: boolean; // Default: false
  autoPageviews?: boolean; // Default: true
  maxRetries?: number; // Default: 10
  retryBackoff?: number; // Default: 1000
}
```

#### Example

```tsx
import { PulsoraProvider } from '@pulsora/react';

function App() {
  return (
    <PulsoraProvider
      config={{
        apiToken: process.env.PULSORA_TOKEN,
        debug: process.env.NODE_ENV === 'development',
      }}
    >
      <YourApp />
    </PulsoraProvider>
  );
}
```

### `usePulsora()`

Access the Pulsora instance directly for advanced use cases.

#### Returns

`PulsoraCore` - The Pulsora tracker instance

#### Example

```tsx
import { usePulsora } from '@pulsora/react';

function AdvancedComponent() {
  const pulsora = usePulsora();

  const handleAction = async () => {
    const fingerprint = await pulsora.getVisitorFingerprint();
    const sessionId = pulsora.getSessionId();

    // Use these values as needed
    console.log({ fingerprint, sessionId });
  };

  return <button onClick={handleAction}>Get Info</button>;
}
```

### `usePageview(options?)`

Automatically track pageviews.

#### Options

```typescript
interface UsePageviewOptions {
  trigger?: any; // Value that triggers pageview when it changes
  disabled?: boolean; // Disable tracking
}
```

#### Example - Basic Usage

```tsx
import { usePageview } from '@pulsora/react';

function Page() {
  usePageview(); // Tracks on mount

  return <div>My Page</div>;
}
```

#### Example - React Router

```tsx
import { useLocation } from 'react-router-dom';
import { usePageview } from '@pulsora/react';

function App() {
  const location = useLocation();

  // Track pageview on route changes
  usePageview({ trigger: location.pathname });

  return (
    <Routes>
      <Route path="/" element={<Home />} />
      <Route path="/about" element={<About />} />
    </Routes>
  );
}
```

#### Example - Next.js App Router

```tsx
'use client';

import { usePathname } from 'next/navigation';
import { usePageview } from '@pulsora/react';

export function AnalyticsProvider({ children }: { children: React.ReactNode }) {
  const pathname = usePathname();

  usePageview({ trigger: pathname });

  return <>{children}</>;
}
```

#### Example - Next.js Pages Router

```tsx
import { useRouter } from 'next/router';
import { usePageview } from '@pulsora/react';

function MyApp({ Component, pageProps }) {
  const router = useRouter();

  usePageview({ trigger: router.asPath });

  return <Component {...pageProps} />;
}
```

#### Example - Remix

```tsx
import { useLocation } from '@remix-run/react';
import { usePageview } from '@pulsora/react';

export function Root() {
  const location = useLocation();

  usePageview({ trigger: location.pathname });

  return (
    <html>
      <head />
      <body>
        <Outlet />
      </body>
    </html>
  );
}
```

### `useEvent()`

Track custom events.

#### Returns

`TrackEventFunction` - Function to track events

```typescript
type TrackEventFunction = (
  eventName: string,
  eventData?: Record<string, any>,
) => Promise<void>;
```

#### Example

```tsx
import { useEvent } from '@pulsora/react';

function ProductCard({ product }) {
  const trackEvent = useEvent();

  const handleAddToCart = () => {
    trackEvent('add_to_cart', {
      product_id: product.id,
      product_name: product.name,
      price: product.price,
      category: product.category,
    });
  };

  const handleViewDetails = () => {
    trackEvent('view_product_details', {
      product_id: product.id,
    });
  };

  return (
    <div>
      <h3>{product.name}</h3>
      <button onClick={handleViewDetails}>View Details</button>
      <button onClick={handleAddToCart}>Add to Cart</button>
    </div>
  );
}
```

#### Example - Form Tracking

```tsx
import { useEvent } from '@pulsora/react';

function ContactForm() {
  const trackEvent = useEvent();

  const handleSubmit = (e) => {
    e.preventDefault();

    trackEvent('form_submit', {
      form_id: 'contact',
      fields: ['name', 'email', 'message'],
    });

    // Submit form...
  };

  const handleFieldFocus = (field) => {
    trackEvent('form_field_focus', { field });
  };

  return (
    <form onSubmit={handleSubmit}>
      <input name="email" onFocus={() => handleFieldFocus('email')} />
      <button type="submit">Submit</button>
    </form>
  );
}
```

### `useIdentify()`

Identify users and manage identification state.

#### Returns

```typescript
interface UseIdentifyReturn {
  identify: (customerId: string) => Promise<void>;
  isIdentified: boolean;
  reset: () => void;
}
```

#### Example - User Login

```tsx
import { useIdentify } from '@pulsora/react';

function LoginForm() {
  const { identify, isIdentified } = useIdentify();

  const handleLogin = async (email, password) => {
    // Perform login logic...
    const user = await api.login(email, password);

    // Identify user for analytics
    await identify(user.id);
  };

  return (
    <div>
      {isIdentified ? (
        <p>Welcome back!</p>
      ) : (
        <button onClick={() => handleLogin(email, password)}>Login</button>
      )}
    </div>
  );
}
```

#### Example - User Logout

```tsx
import { useIdentify } from '@pulsora/react';

function LogoutButton() {
  const { reset } = useIdentify();

  const handleLogout = () => {
    // Perform logout logic...
    api.logout();

    // Reset analytics identification
    reset();
  };

  return <button onClick={handleLogout}>Logout</button>;
}
```

#### Example - Auto-identify from Auth Context

```tsx
import { useEffect } from 'react';
import { useAuth } from './auth-context';
import { useIdentify } from '@pulsora/react';

function AutoIdentify() {
  const { user } = useAuth();
  const { identify, isIdentified } = useIdentify();

  useEffect(() => {
    if (user && !isIdentified) {
      identify(user.id);
    }
  }, [user, isIdentified, identify]);

  return null;
}

// Use in your app:
function App() {
  return (
    <AuthProvider>
      <PulsoraProvider config={{ apiToken: 'your-token' }}>
        <AutoIdentify />
        <YourApp />
      </PulsoraProvider>
    </AuthProvider>
  );
}
```

## Complete Examples

### Basic React App

```tsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import { PulsoraProvider, usePageview, useEvent } from '@pulsora/react';

function Home() {
  usePageview();
  const trackEvent = useEvent();

  return (
    <div>
      <h1>Welcome</h1>
      <button onClick={() => trackEvent('cta_click')}>Get Started</button>
    </div>
  );
}

function App() {
  return (
    <PulsoraProvider config={{ apiToken: 'your-api-token' }}>
      <Home />
    </PulsoraProvider>
  );
}

ReactDOM.createRoot(document.getElementById('root')!).render(<App />);
```

### React Router SPA

```tsx
import { BrowserRouter, Routes, Route, useLocation } from 'react-router-dom';
import { PulsoraProvider, usePageview, useEvent } from '@pulsora/react';

function Analytics() {
  const location = useLocation();
  usePageview({ trigger: location.pathname });
  return null;
}

function App() {
  return (
    <PulsoraProvider config={{ apiToken: 'your-api-token' }}>
      <BrowserRouter>
        <Analytics />
        <Routes>
          <Route path="/" element={<Home />} />
          <Route path="/about" element={<About />} />
          <Route path="/contact" element={<Contact />} />
        </Routes>
      </BrowserRouter>
    </PulsoraProvider>
  );
}
```

### Next.js App

```tsx
// app/providers.tsx
'use client';

import { PulsoraProvider } from '@pulsora/react';
import { usePathname } from 'next/navigation';
import { usePageview } from '@pulsora/react';
import { useEffect } from 'react';

function PageviewTracker() {
  const pathname = usePathname();
  usePageview({ trigger: pathname });
  return null;
}

export function Providers({ children }: { children: React.ReactNode }) {
  return (
    <PulsoraProvider
      config={{
        apiToken: process.env.NEXT_PUBLIC_PULSORA_TOKEN!,
      }}
    >
      <PageviewTracker />
      {children}
    </PulsoraProvider>
  );
}

// app/layout.tsx
import { Providers } from './providers';

export default function RootLayout({ children }) {
  return (
    <html>
      <body>
        <Providers>{children}</Providers>
      </body>
    </html>
  );
}
```

## TypeScript Support

Full TypeScript support with exported types:

```tsx
import type {
  PulsoraConfig,
  PulsoraCore,
  EventData,
  UsePageviewOptions,
  TrackEventFunction,
  UseIdentifyReturn,
} from '@pulsora/react';
```

## SSR Compatibility

The package is safe for server-side rendering. The Pulsora instance will only initialize in the browser.

## Testing

Each package has its own test suite. Run tests from the package directory:

```bash
cd pulsora-packages/react
npm test
```

## Bundle Size

- **ESM**: ~2KB (uncompressed, excluding peer deps)
- **Gzipped**: ~800 bytes (excluding peer deps)

Peer dependencies (`@pulsora/core` and `react`) are not included in the bundle.

## Best Practices

### 1. Single Provider

Only use one `<PulsoraProvider>` at the root of your app:

```tsx
// ✅ Good
<PulsoraProvider config={{ apiToken: 'token' }}>
  <App />
</PulsoraProvider>

// ❌ Bad - don't nest providers
<PulsoraProvider config={{ apiToken: 'token' }}>
  <PulsoraProvider config={{ apiToken: 'token' }}>
    <App />
  </PulsoraProvider>
</PulsoraProvider>
```

### 2. Route Change Tracking

For SPAs, use `usePageview` with a trigger at the router level, not in individual pages:

```tsx
// ✅ Good - track at router level
function App() {
  const location = useLocation();
  usePageview({ trigger: location.pathname });

  return <Routes>...</Routes>;
}

// ❌ Inefficient - tracking in each page
function Page1() {
  usePageview(); // Don't do this in every page
  return <div>Page 1</div>;
}
```

### 3. Event Naming

Use clear, consistent event names:

```tsx
// ✅ Good
trackEvent('button_click', { button_id: 'cta' });
trackEvent('form_submit', { form_id: 'contact' });
trackEvent('add_to_cart', { product_id: 123 });

// ❌ Bad - vague names
trackEvent('click');
trackEvent('action');
```

### 4. Identify Early

Identify users as soon as they log in:

```tsx
function LoginPage() {
  const { identify } = useIdentify();

  const handleLogin = async (credentials) => {
    const user = await api.login(credentials);
    await identify(user.id); // Identify immediately
  };

  return <LoginForm onSubmit={handleLogin} />;
}
```

## Troubleshooting

### Provider Not Found Error

If you see `usePulsora must be used within a PulsoraProvider`, make sure:

1. You have `<PulsoraProvider>` wrapping your app
2. The hook is called inside a component that's a child of the provider

### Pageviews Not Tracked

If pageviews aren't being tracked:

1. Check that `autoPageviews` is not disabled in config
2. If using `usePageview` with a trigger, verify the trigger value changes
3. Check browser console for errors (enable `debug: true`)

### TypeScript Errors

If you get TypeScript errors about missing types:

```bash
npm install --save-dev @types/react
```

## License

MIT

## Support

- Documentation: https://pulsora.co/docs
- Issues: https://github.com/pulsora/pulsora/issues
- Email: support@pulsora.co