React Examples#

Use these recipes to wire Pulsora into production-ready React apps.

Global Provider + Hooks#

// src/main.tsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import { PulsoraProvider } from '@pulsora/react';
import { App } from './App';

ReactDOM.createRoot(document.getElementById('root')!).render(
  <React.StrictMode>
    <PulsoraProvider config={{ apiToken: import.meta.env.VITE_PULSORA_TOKEN }}>
      <App />
    </PulsoraProvider>
  </React.StrictMode>,
);
// src/App.tsx
import { Routes, Route, useLocation } from 'react-router-dom';
import { useEvent, usePageview } from '@pulsora/react';

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

function Pricing() {
  const trackEvent = useEvent();
  return (
    <button onClick={() => trackEvent('cta_click', { plan: 'pro' })}>
      Get Started
    </button>
  );
}

export function App() {
  return (
    <>
      <AnalyticsBridge />
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/pricing" element={<Pricing />} />
      </Routes>
    </>
  );
}

Capturing Checkout Metadata#

import { useEvent, usePulsora } from '@pulsora/react';

function CheckoutButton({ priceId }: { priceId: string }) {
  const trackEvent = useEvent();
  const pulsora = usePulsora();

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

    await fetch('/api/checkout/session', {
      method: 'POST',
      body: JSON.stringify({
        priceId,
        visitor_fingerprint: fingerprint,
        session_id: sessionId,
      }),
    });

    trackEvent('checkout_start', { priceId });
  };

  return <button onClick={handleCheckout}>Buy Now</button>;
}

Conditional Event Tracking#

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

function TrialSignupForm() {
  const trackEvent = useEvent();
  const [email, setEmail] = useState('');

  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault();

    // Only track valid submissions
    if (email.includes('@')) {
      await trackEvent('trial_signup', { email_domain: email.split('@')[1] });
    }

    // ...submit to backend
  };

  return (
    <form onSubmit={handleSubmit} className="space-y-4">
      <input
        className="input"
        placeholder="Work email"
        value={email}
        onChange={(e) => setEmail(e.target.value)}
      />
      <button className="btn btn-primary" type="submit">
        Start trial
      </button>
    </form>
  );
}

Next.js Layout Pattern#

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

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en">
      <body>
        <Providers>{children}</Providers>
      </body>
    </html>
  );
}

See the SSR guide for the full implementation.

Need deeper explanations? Head back to the hooks reference →